Actual source code: client.c

petsc-3.13.6 2020-09-29
Report Typos and Errors

  2:  #include <petscwebclient.h>
  3: #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  4: #pragma gcc diagnostic ignored "-Wdeprecated-declarations"

  6: static BIO *bio_err = NULL;

  8: #define PASSWORD "password"

 10: #if defined(PETSC_USE_SSL_CERTIFICATE)
 11: static int password_cb(char *buf,int num, int rwflag,void *userdata)
 12: {
 13:   if (num < strlen(PASSWORD)+1) return(0);
 14:   strcpy(buf,PASSWORD);
 15:   return(strlen(PASSWORD));
 16: }
 17: #endif

 19: static void sigpipe_handle(int x)
 20: {
 21: }

 23: /*@C
 24:     PetscSSLInitializeContext - Set up an SSL context suitable for initiating HTTPS requests.

 26:     Output Parameter:
 27: .   octx - the SSL_CTX to be passed to PetscHTTPSConnect

 29:     Level: advanced

 31:     If PETSc was ./configure -with-ssl-certificate requires the user have created a self-signed certificate with
 32: $    saws/CA.pl  -newcert  (using the passphrase of password)
 33: $    cat newkey.pem newcert.pem > sslclient.pem

 35:     and put the resulting file in either the current directory (with the Section 1.5 Writing Application Codes with PETSc) or in the home directory. This seems kind of
 36:     silly but it was all I could figure out.

 38: .seealso: PetscSSLDestroyContext(), PetscHTTPSConnect(), PetscHTTPSRequest()

 40: @*/
 41: PetscErrorCode PetscSSLInitializeContext(SSL_CTX **octx)
 42: {
 43:     SSL_CTX        *ctx;
 44: #if defined(PETSC_USE_SSL_CERTIFICATE)
 45:     char           keyfile[PETSC_MAX_PATH_LEN];
 46:     PetscBool      exists;
 48: #endif

 51:     if (!bio_err){
 52:       SSL_library_init();
 53:       SSL_load_error_strings();
 54:       bio_err = BIO_new_fp(stderr,BIO_NOCLOSE);
 55:     }

 57:     /* Set up a SIGPIPE handler */
 58:     signal(SIGPIPE,sigpipe_handle);

 60: /* suggested at https://mta.openssl.org/pipermail/openssl-dev/2015-May/001449.html */
 61: #if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
 62:     ctx  = SSL_CTX_new(TLS_client_method());
 63: #else
 64:     ctx  = SSL_CTX_new(SSLv23_client_method());
 65: #endif
 66:     SSL_CTX_set_mode(ctx,SSL_MODE_AUTO_RETRY);

 68: #if defined(PETSC_USE_SSL_CERTIFICATE)
 69:     /* Locate keyfile */
 70:     PetscStrcpy(keyfile,"sslclient.pem");
 71:     PetscTestFile(keyfile,'r',&exists);
 72:     if (!exists) {
 73:       PetscGetHomeDirectory(keyfile,PETSC_MAX_PATH_LEN);
 74:       PetscStrcat(keyfile,"/");
 75:       PetscStrcat(keyfile,"sslclient.pem");
 76:       PetscTestFile(keyfile,'r',&exists);
 77:       if (!exists) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to locate sslclient.pem file in current directory or home directory");
 78:     }

 80:     /* Load our keys and certificates*/
 81:     if (!(SSL_CTX_use_certificate_chain_file(ctx,keyfile))) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Cannot read certificate file");

 83:     SSL_CTX_set_default_passwd_cb(ctx,password_cb);
 84:     if (!(SSL_CTX_use_PrivateKey_file(ctx,keyfile,SSL_FILETYPE_PEM))) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Cannot read key file");
 85: #endif

 87:     *octx = ctx;
 88:     return(0);
 89: }

 91: /*@C
 92:      PetscSSLDestroyContext - frees a SSL_CTX obtained with PetscSSLInitializeContext() 

 94:      Input Parameter:
 95: .     ctx - the SSL_CTX

 97:     Level: advanced

 99: .seealso: PetscSSLInitializeContext(), PetscHTTPSConnect()
100: @*/
101: PetscErrorCode PetscSSLDestroyContext(SSL_CTX *ctx)
102: {
104:   SSL_CTX_free(ctx);
105:   return(0);
106: }

108: static PetscErrorCode PetscHTTPBuildRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],char **outrequest)
109: {
110:   char           *request=0;
111:   char           contentlength[40],contenttype[80],*path,*host;
112:   size_t         request_len,headlen,bodylen,contentlen,pathlen,hostlen,typelen,contenttypelen = 0;
114:   PetscBool      flg;

117:   PetscStrallocpy(url,&host);
118:   PetscStrchr(host,'/',&path);
119:   if (!path) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE,"url must contain / it is %s",url);
120:   *path = 0;
121:   PetscStrlen(host,&hostlen);

123:   PetscStrchr(url,'/',&path);
124:   PetscStrlen(path,&pathlen);

126:   if (header) {
127:     PetscStrendswith(header,"\r\n",&flg);
128:     if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"header must end with \\r\\n");
129:   }

131:   PetscStrlen(type,&typelen);
132:   if (ctype) {
133:     PetscSNPrintf(contenttype,80,"Content-Type: %s\r\n",ctype);
134:     PetscStrlen(contenttype,&contenttypelen);
135:   }
136:   PetscStrlen(header,&headlen);
137:   PetscStrlen(body,&bodylen);
138:   PetscSNPrintf(contentlength,40,"Content-Length: %d\r\n\r\n",(int)bodylen);
139:   PetscStrlen(contentlength,&contentlen);

141:   /* Now construct our HTTP request */
142:   request_len = typelen + 1 + pathlen + hostlen + 100 + headlen + contenttypelen + contentlen + bodylen + 1;
143:   PetscMalloc1(request_len,&request);
144:   PetscStrcpy(request,type);
145:   PetscStrcat(request," ");
146:   PetscStrcat(request,path);
147:   PetscStrcat(request," HTTP/1.1\r\nHost: ");
148:   PetscStrcat(request,host);
149:   PetscFree(host);
150:   PetscStrcat(request,"\r\nUser-Agent:PETScClient\r\n");
151:   PetscStrcat(request,header);
152:   if (ctype) {
153:     PetscStrcat(request,contenttype);
154:   }
155:   PetscStrcat(request,contentlength);
156:   PetscStrcat(request,body);
157:   PetscStrlen(request,&request_len);
158:   PetscInfo1(NULL,"HTTPS request follows: \n%s\n",request);

160:   *outrequest = request;
161:   return(0);
162: }


165: /*@C
166:      PetscHTTPSRequest - Send a request to an HTTPS server

168:    Input Parameters:
169: +   type - either "POST" or "GET"
170: .   url -  URL of request host/path
171: .   header - additional header information, may be NULL
172: .   ctype - data type of body, for example Section 1.5 Writing Application Codes with PETSc/json
173: .   body - data to send to server
174: .   ssl - obtained with PetscHTTPSConnect()
175: -   buffsize - size of buffer

177:    Output Parameter:
178: .   buff - everything returned from server

180:     Level: advanced

182: .seealso: PetscHTTPRequest(), PetscHTTPSConnect(), PetscSSLInitializeContext(), PetscSSLDestroyContext(), PetscPullJSONValue()

184: @*/
185: PetscErrorCode PetscHTTPSRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],SSL *ssl,char buff[],size_t buffsize)
186: {
187:   char           *request;
188:   int            r;
189:   size_t         request_len,len;
191:   PetscBool      foundbody = PETSC_FALSE;

194:   PetscHTTPBuildRequest(type,url,header,ctype,body,&request);
195:   PetscStrlen(request,&request_len);

197:   r = SSL_write(ssl,request,(int)request_len);
198:   switch (SSL_get_error(ssl,r)){
199:     case SSL_ERROR_NONE:
200:       if (request_len != (size_t)r) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Incomplete write to SSL socket");
201:       break;
202:     default:
203:       SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL socket write problem");
204:   }

206:   /* Now read the server's response, globus sends it in two chunks hence must read a second time if needed */
207:   PetscArrayzero(buff,buffsize);
208:   len       = 0;
209:   foundbody = PETSC_FALSE;
210:   do {
211:     char   *clen;
212:     int    cl;
213:     size_t nlen;

215:     r = SSL_read(ssl,buff+len,(int)buffsize);
216:     len += r;
217:     switch (SSL_get_error(ssl,r)){
218:     case SSL_ERROR_NONE:
219:       break;
220:     case SSL_ERROR_ZERO_RETURN:
221:       foundbody = PETSC_TRUE;
222:       SSL_shutdown(ssl);
223:       break;
224:     case SSL_ERROR_SYSCALL:
225:       foundbody = PETSC_TRUE;
226:       break;
227:     default:
228:       SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL read problem");
229:     }

231:     PetscStrstr(buff,"Content-Length: ",&clen);
232:     if (clen) {
233:       clen += 15;
234:       sscanf(clen,"%d",&cl);
235:       if (!cl) foundbody = PETSC_TRUE;
236:       else {
237:         PetscStrstr(buff,"\r\n\r\n",&clen);
238:         if (clen) {
239:           PetscStrlen(clen,&nlen);
240:           if (nlen-4 == (size_t) cl) foundbody = PETSC_TRUE;
241:         }
242:       }
243:     } else {
244:       /* if no content length than must leave because you don't know if you can read again */
245:       foundbody = PETSC_TRUE;
246:     }
247:   } while (!foundbody);
248:   PetscInfo1(NULL,"HTTPS result follows: \n%s\n",buff);

250:   SSL_free(ssl);
251:   PetscFree(request);
252:   return(0);
253: }

255: /*@C
256:      PetscHTTPRequest - Send a request to an HTTP server

258:    Input Parameters:
259: +   type - either "POST" or "GET"
260: .   url -  URL of request host/path
261: .   header - additional header information, may be NULL
262: .   ctype - data type of body, for example Section 1.5 Writing Application Codes with PETSc/json
263: .   body - data to send to server
264: .   sock - obtained with PetscOpenSocket()
265: -   buffsize - size of buffer

267:    Output Parameter:
268: .   buff - everything returned from server

270:     Level: advanced

272: .seealso: PetscHTTPSRequest(), PetscOpenSocket(), PetscHTTPSConnect(), PetscPullJSONValue()
273: @*/
274: PetscErrorCode PetscHTTPRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],int sock,char buff[],size_t buffsize)
275: {
276:   char           *request;
277:   size_t         request_len;

281:   PetscHTTPBuildRequest(type,url,header,ctype,body,&request);
282:   PetscStrlen(request,&request_len);

284:   PetscBinaryWrite(sock,request,request_len,PETSC_CHAR);
285:   PetscFree(request);
286:   PetscBinaryRead(sock,buff,buffsize,NULL,PETSC_CHAR);
287:   buff[buffsize-1] = 0;
288:   PetscInfo1(NULL,"HTTP result follows: \n%s\n",buff);
289:   return(0);
290: }

292: /*@C
293:       PetscHTTPSConnect - connect to a HTTPS server

295:     Input Parameters:
296: +    host - the name of the machine hosting the HTTPS server
297: .    port - the port number where the server is hosting, usually 443
298: -    ctx - value obtained with PetscSSLInitializeContext()

300:     Output Parameters:
301: +    sock - socket to connect
302: -    ssl - the argument passed to PetscHTTPSRequest()

304:     Level: advanced

306: .seealso: PetscOpenSocket(), PetscHTTPSRequest(), PetscSSLInitializeContext()
307: @*/
308: PetscErrorCode PetscHTTPSConnect(const char host[],int port,SSL_CTX *ctx,int *sock,SSL **ssl)
309: {
310:   BIO            *sbio;

314:   /* Connect the TCP socket*/
315:   PetscOpenSocket(host,port,sock);

317:   /* Connect the SSL socket */
318:   *ssl = SSL_new(ctx);
319:   sbio = BIO_new_socket(*sock,BIO_NOCLOSE);
320:   SSL_set_bio(*ssl,sbio,sbio);
321:   if (SSL_connect(*ssl) <= 0) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL connect error");
322:   return(0);
323: }

325: /*@C
326:      PetscPullJSONValue - Given a JSON response containing the substring with "key" : "value"  where there may or not be spaces around the : returns the value.

328:     Input Parameters:
329: +    buff - the char array containing the possible values
330: .    key - the key of the requested value
331: -    valuelen - the length of the array to contain the value associated with the key

333:     Output Parameters:
334: +    value - the value obtained
335: -    found - flag indicating if the value was found in the buff

337:     Level: advanced

339: @*/
340: PetscErrorCode PetscPullJSONValue(const char buff[],const char key[],char value[],size_t valuelen,PetscBool *found)
341: {
343:   char           *v,*w;
344:   char           work[256];
345:   size_t         len;

348:   PetscStrcpy(work,"\"");
349:   PetscStrlcat(work,key,sizeof(work));
350:   PetscStrcat(work,"\":");
351:   PetscStrstr(buff,work,&v);
352:   PetscStrlen(work,&len);
353:   if (v) {
354:     v += len;
355:   } else {
356:     work[len++-1] = 0;
357:     PetscStrcat(work," :");
358:     PetscStrstr(buff,work,&v);
359:     if (!v) {
360:       *found = PETSC_FALSE;
361:       return(0);
362:     }
363:     v += len;
364:   }
365:   PetscStrchr(v,'\"',&v);
366:   if (!v) {
367:     *found = PETSC_FALSE;
368:     return(0);
369:   }
370:   PetscStrchr(v+1,'\"',&w);
371:   if (!w) {
372:     *found = PETSC_FALSE;
373:     return(0);
374:   }
375:   *found = PETSC_TRUE;
376:   PetscStrncpy(value,v+1,PetscMin((size_t)(w-v),valuelen));
377:   return(0);
378: }

380: #include <ctype.h>

382: /*@C
383:     PetscPushJSONValue -  Puts a "key" : "value" pair onto a string

385:     Input Parameters:
386: +   buffer - the char array where the value will be put
387: .   key - the key value to be set
388: .   value - the value associated with the key
389: -   bufflen - the size of the buffer (currently ignored)

391:     Level: advanced

393:     Notes:
394:     Ignores lengths so can cause buffer overflow
395: @*/
396: PetscErrorCode PetscPushJSONValue(char buff[],const char key[],const char value[],size_t bufflen)
397: {
399:   size_t         len;
400:   PetscBool      special;

403:   PetscStrcmp(value,"null",&special);
404:   if (!special) {
405:     PetscStrcmp(value,"true",&special);
406:   }
407:   if (!special) {
408:     PetscStrcmp(value,"false",&special);
409:   }
410:   if (!special) {
411:     PetscInt i;

413:     PetscStrlen(value,&len);
414:     special = PETSC_TRUE;
415:     for (i=0; i<(int)len; i++) {
416:       if (!isdigit(value[i])) {
417:         special = PETSC_FALSE;
418:         break;
419:       }
420:     }
421:   }

423:   PetscStrcat(buff,"\"");
424:   PetscStrcat(buff,key);
425:   PetscStrcat(buff,"\":");
426:   if (!special) {
427:     PetscStrcat(buff,"\"");
428:   }
429:   PetscStrcat(buff,value);
430:   if (!special) {
431:     PetscStrcat(buff,"\"");
432:   }
433:   return(0);
434: }