Actual source code: client.c


  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 application) 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;
 47: #endif

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

 55:     /* Set up a SIGPIPE handler */
 56:     signal(SIGPIPE,sigpipe_handle);

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

 66: #if defined(PETSC_USE_SSL_CERTIFICATE)
 67:     /* Locate keyfile */
 68:     PetscStrcpy(keyfile,"sslclient.pem");
 69:     PetscTestFile(keyfile,'r',&exists);
 70:     if (!exists) {
 71:       PetscGetHomeDirectory(keyfile,PETSC_MAX_PATH_LEN);
 72:       PetscStrcat(keyfile,"/");
 73:       PetscStrcat(keyfile,"sslclient.pem");
 74:       PetscTestFile(keyfile,'r',&exists);
 76:     }

 78:     /* Load our keys and certificates*/

 81:     SSL_CTX_set_default_passwd_cb(ctx,password_cb);
 83: #endif

 85:     *octx = ctx;
 86:     return 0;
 87: }

 89: /*@C
 90:      PetscSSLDestroyContext - frees a SSL_CTX obtained with PetscSSLInitializeContext()

 92:      Input Parameter:
 93: .     ctx - the SSL_CTX

 95:     Level: advanced

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

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

112:   PetscStrallocpy(url,&host);
113:   PetscStrchr(host,'/',&path);
115:   *path = 0;
116:   PetscStrlen(host,&hostlen);

118:   PetscStrchr(url,'/',&path);
119:   PetscStrlen(path,&pathlen);

121:   if (header) {
122:     PetscStrendswith(header,"\r\n",&flg);
124:   }

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

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

155:   *outrequest = request;
156:   return 0;
157: }

159: /*@C
160:      PetscHTTPSRequest - Send a request to an HTTPS server

162:    Input Parameters:
163: +   type - either "POST" or "GET"
164: .   url -  URL of request host/path
165: .   header - additional header information, may be NULL
166: .   ctype - data type of body, for example application/json
167: .   body - data to send to server
168: .   ssl - obtained with PetscHTTPSConnect()
169: -   buffsize - size of buffer

171:    Output Parameter:
172: .   buff - everything returned from server

174:     Level: advanced

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

178: @*/
179: PetscErrorCode PetscHTTPSRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],SSL *ssl,char buff[],size_t buffsize)
180: {
181:   char           *request;
182:   int            r;
183:   size_t         request_len,len;
184:   PetscBool      foundbody = PETSC_FALSE;

186:   PetscHTTPBuildRequest(type,url,header,ctype,body,&request);
187:   PetscStrlen(request,&request_len);

189:   r = SSL_write(ssl,request,(int)request_len);
190:   switch (SSL_get_error(ssl,r)) {
191:     case SSL_ERROR_NONE:
193:       break;
194:     default:
195:       SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL socket write problem");
196:   }

198:   /* Now read the server's response, globus sends it in two chunks hence must read a second time if needed */
199:   PetscArrayzero(buff,buffsize);
200:   len       = 0;
201:   foundbody = PETSC_FALSE;
202:   do {
203:     char   *clen;
204:     int    cl;
205:     size_t nlen;

207:     r = SSL_read(ssl,buff+len,(int)buffsize);
208:     len += r;
209:     switch (SSL_get_error(ssl,r)) {
210:     case SSL_ERROR_NONE:
211:       break;
212:     case SSL_ERROR_ZERO_RETURN:
213:       foundbody = PETSC_TRUE;
214:       SSL_shutdown(ssl);
215:       break;
216:     case SSL_ERROR_SYSCALL:
217:       foundbody = PETSC_TRUE;
218:       break;
219:     default:
220:       SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL read problem");
221:     }

223:     PetscStrstr(buff,"Content-Length: ",&clen);
224:     if (clen) {
225:       clen += 15;
226:       sscanf(clen,"%d",&cl);
227:       if (!cl) foundbody = PETSC_TRUE;
228:       else {
229:         PetscStrstr(buff,"\r\n\r\n",&clen);
230:         if (clen) {
231:           PetscStrlen(clen,&nlen);
232:           if (nlen-4 == (size_t) cl) foundbody = PETSC_TRUE;
233:         }
234:       }
235:     } else {
236:       /* if no content length than must leave because you don't know if you can read again */
237:       foundbody = PETSC_TRUE;
238:     }
239:   } while (!foundbody);
240:   PetscInfo(NULL,"HTTPS result follows: \n%s\n",buff);

242:   SSL_free(ssl);
243:   PetscFree(request);
244:   return 0;
245: }

247: /*@C
248:      PetscHTTPRequest - Send a request to an HTTP server

250:    Input Parameters:
251: +   type - either "POST" or "GET"
252: .   url -  URL of request host/path
253: .   header - additional header information, may be NULL
254: .   ctype - data type of body, for example application/json
255: .   body - data to send to server
256: .   sock - obtained with PetscOpenSocket()
257: -   buffsize - size of buffer

259:    Output Parameter:
260: .   buff - everything returned from server

262:     Level: advanced

264: .seealso: PetscHTTPSRequest(), PetscOpenSocket(), PetscHTTPSConnect(), PetscPullJSONValue()
265: @*/
266: PetscErrorCode PetscHTTPRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],int sock,char buff[],size_t buffsize)
267: {
268:   char           *request;
269:   size_t         request_len;

271:   PetscHTTPBuildRequest(type,url,header,ctype,body,&request);
272:   PetscStrlen(request,&request_len);

274:   PetscBinaryWrite(sock,request,request_len,PETSC_CHAR);
275:   PetscFree(request);
276:   PetscBinaryRead(sock,buff,buffsize,NULL,PETSC_CHAR);
277:   buff[buffsize-1] = 0;
278:   PetscInfo(NULL,"HTTP result follows: \n%s\n",buff);
279:   return 0;
280: }

282: /*@C
283:       PetscHTTPSConnect - connect to a HTTPS server

285:     Input Parameters:
286: +    host - the name of the machine hosting the HTTPS server
287: .    port - the port number where the server is hosting, usually 443
288: -    ctx - value obtained with PetscSSLInitializeContext()

290:     Output Parameters:
291: +    sock - socket to connect
292: -    ssl - the argument passed to PetscHTTPSRequest()

294:     Level: advanced

296: .seealso: PetscOpenSocket(), PetscHTTPSRequest(), PetscSSLInitializeContext()
297: @*/
298: PetscErrorCode PetscHTTPSConnect(const char host[],int port,SSL_CTX *ctx,int *sock,SSL **ssl)
299: {
300:   BIO            *sbio;

302:   /* Connect the TCP socket*/
303:   PetscOpenSocket(host,port,sock);

305:   /* Connect the SSL socket */
306:   *ssl = SSL_new(ctx);
307:   sbio = BIO_new_socket(*sock,BIO_NOCLOSE);
308:   SSL_set_bio(*ssl,sbio,sbio);
310:   return 0;
311: }

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

316:     Input Parameters:
317: +    buff - the char array containing the possible values
318: .    key - the key of the requested value
319: -    valuelen - the length of the array to contain the value associated with the key

321:     Output Parameters:
322: +    value - the value obtained
323: -    found - flag indicating if the value was found in the buff

325:     Level: advanced

327: @*/
328: PetscErrorCode PetscPullJSONValue(const char buff[],const char key[],char value[],size_t valuelen,PetscBool *found)
329: {
330:   char           *v,*w;
331:   char           work[256];
332:   size_t         len;

334:   PetscStrcpy(work,"\"");
335:   PetscStrlcat(work,key,sizeof(work));
336:   PetscStrcat(work,"\":");
337:   PetscStrstr(buff,work,&v);
338:   PetscStrlen(work,&len);
339:   if (v) {
340:     v += len;
341:   } else {
342:     work[len++-1] = 0;
343:     PetscStrcat(work," :");
344:     PetscStrstr(buff,work,&v);
345:     if (!v) {
346:       *found = PETSC_FALSE;
347:       return 0;
348:     }
349:     v += len;
350:   }
351:   PetscStrchr(v,'\"',&v);
352:   if (!v) {
353:     *found = PETSC_FALSE;
354:     return 0;
355:   }
356:   PetscStrchr(v+1,'\"',&w);
357:   if (!w) {
358:     *found = PETSC_FALSE;
359:     return 0;
360:   }
361:   *found = PETSC_TRUE;
362:   PetscStrncpy(value,v+1,PetscMin((size_t)(w-v),valuelen));
363:   return 0;
364: }

366: #include <ctype.h>

368: /*@C
369:     PetscPushJSONValue -  Puts a "key" : "value" pair onto a string

371:     Input Parameters:
372: +   buffer - the char array where the value will be put
373: .   key - the key value to be set
374: .   value - the value associated with the key
375: -   bufflen - the size of the buffer (currently ignored)

377:     Level: advanced

379:     Notes:
380:     Ignores lengths so can cause buffer overflow
381: @*/
382: PetscErrorCode PetscPushJSONValue(char buff[],const char key[],const char value[],size_t bufflen)
383: {
384:   size_t         len;
385:   PetscBool      special;

387:   PetscStrcmp(value,"null",&special);
388:   if (!special) {
389:     PetscStrcmp(value,"true",&special);
390:   }
391:   if (!special) {
392:     PetscStrcmp(value,"false",&special);
393:   }
394:   if (!special) {
395:     PetscInt i;

397:     PetscStrlen(value,&len);
398:     special = PETSC_TRUE;
399:     for (i=0; i<(int)len; i++) {
400:       if (!isdigit(value[i])) {
401:         special = PETSC_FALSE;
402:         break;
403:       }
404:     }
405:   }

407:   PetscStrcat(buff,"\"");
408:   PetscStrcat(buff,key);
409:   PetscStrcat(buff,"\":");
410:   if (!special) {
411:     PetscStrcat(buff,"\"");
412:   }
413:   PetscStrcat(buff,value);
414:   if (!special) {
415:     PetscStrcat(buff,"\"");
416:   }
417:   return 0;
418: }