Date: 2022-03-31T04:46:19+00:00

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: }