Date: 2022-03-04T17:37: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;
     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: }

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

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

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

    179:     Level: advanced

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

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

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

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

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

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

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

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

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

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

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

    269:     Level: advanced

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

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

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

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

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

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

    303:     Level: advanced

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

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

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

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

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

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

    336:     Level: advanced

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

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

    379: #include <ctype.h>

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

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

    390:     Level: advanced

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

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

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

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