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) { }

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

 24:     Output Parameter:
 25: .   octx - the SSL_CTX to be passed to `PetscHTTPSConnect90`

 27:     Level: advanced

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

 33:     and put the resulting file in either the current directory (with the application) or in the home directory. This seems kind of
 34:     silly but it was all I could figure out.

 36: .seealso: `PetscSSLDestroyContext()`, `PetscHTTPSConnect()`, `PetscHTTPSRequest()`
 37: @*/
 38: PetscErrorCode PetscSSLInitializeContext(SSL_CTX **octx)
 39: {
 40:   SSL_CTX *ctx;
 41: #if defined(PETSC_USE_SSL_CERTIFICATE)
 42:   char      keyfile[PETSC_MAX_PATH_LEN];
 43:   PetscBool exists;
 44: #endif

 46:   if (!bio_err) {
 47:     SSL_library_init();
 48:     SSL_load_error_strings();
 49:     bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
 50:   }

 52:   /* Set up a SIGPIPE handler */
 53:   signal(SIGPIPE, sigpipe_handle);

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

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

 75:   /* Load our keys and certificates*/

 78:   SSL_CTX_set_default_passwd_cb(ctx, password_cb);
 80: #endif

 82:   *octx = ctx;
 83:   return 0;
 84: }

 86: /*@C
 87:      PetscSSLDestroyContext - frees a `SSL_CTX` obtained with `PetscSSLInitializeContext()`

 89:      Input Parameter:
 90: .     ctx - the `SSL_CTX`

 92:     Level: advanced

 94: .seealso: `PetscSSLInitializeContext()`, `PetscHTTPSConnect()`
 95: @*/
 96: PetscErrorCode PetscSSLDestroyContext(SSL_CTX *ctx)
 97: {
 98:   SSL_CTX_free(ctx);
 99:   return 0;
100: }

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

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

115:   PetscStrchr(url, '/', &path);
116:   PetscStrlen(path, &pathlen);

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

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

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

150:   *outrequest = request;
151:   return 0;
152: }

154: /*@C
155:      PetscHTTPSRequest - Send a request to an HTTPS server

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

166:    Output Parameter:
167: .   buff - everything returned from server

169:     Level: advanced

171: .seealso: `PetscHTTPRequest()`, `PetscHTTPSConnect()`, `PetscSSLInitializeContext()`, `PetscSSLDestroyContext()`, `PetscPullJSONValue()`
172: @*/
173: PetscErrorCode PetscHTTPSRequest(const char type[], const char url[], const char header[], const char ctype[], const char body[], SSL *ssl, char buff[], size_t buffsize)
174: {
175:   char     *request;
176:   int       r;
177:   size_t    request_len, len;
178:   PetscBool foundbody = PETSC_FALSE;

180:   PetscHTTPBuildRequest(type, url, header, ctype, body, &request);
181:   PetscStrlen(request, &request_len);

183:   r = SSL_write(ssl, request, (int)request_len);
184:   switch (SSL_get_error(ssl, r)) {
185:   case SSL_ERROR_NONE:
187:     break;
188:   default:
189:     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, "SSL socket write problem");
190:   }

192:   /* Now read the server's response, globus sends it in two chunks hence must read a second time if needed */
193:   PetscArrayzero(buff, buffsize);
194:   len       = 0;
195:   foundbody = PETSC_FALSE;
196:   do {
197:     char  *clen;
198:     int    cl;
199:     size_t nlen;

201:     r = SSL_read(ssl, buff + len, (int)buffsize);
202:     len += r;
203:     switch (SSL_get_error(ssl, r)) {
204:     case SSL_ERROR_NONE:
205:       break;
206:     case SSL_ERROR_ZERO_RETURN:
207:       foundbody = PETSC_TRUE;
208:       SSL_shutdown(ssl);
209:       break;
210:     case SSL_ERROR_SYSCALL:
211:       foundbody = PETSC_TRUE;
212:       break;
213:     default:
214:       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_LIB, "SSL read problem");
215:     }

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

236:   SSL_free(ssl);
237:   PetscFree(request);
238:   return 0;
239: }

241: /*@C
242:      PetscHTTPRequest - Send a request to an HTTP server

244:    Input Parameters:
245: +   type - either "POST" or "GET"
246: .   url -  URL of request host/path
247: .   header - additional header information, may be NULL
248: .   ctype - data type of body, for example application/json
249: .   body - data to send to server
250: .   sock - obtained with `PetscOpenSocket()`
251: -   buffsize - size of buffer

253:    Output Parameter:
254: .   buff - everything returned from server

256:     Level: advanced

258: .seealso: `PetscHTTPSRequest()`, `PetscOpenSocket()`, `PetscHTTPSConnect()`, `PetscPullJSONValue()`
259: @*/
260: PetscErrorCode PetscHTTPRequest(const char type[], const char url[], const char header[], const char ctype[], const char body[], int sock, char buff[], size_t buffsize)
261: {
262:   char  *request;
263:   size_t request_len;

265:   PetscHTTPBuildRequest(type, url, header, ctype, body, &request);
266:   PetscStrlen(request, &request_len);

268:   PetscBinaryWrite(sock, request, request_len, PETSC_CHAR);
269:   PetscFree(request);
270:   PetscBinaryRead(sock, buff, buffsize, NULL, PETSC_CHAR);
271:   buff[buffsize - 1] = 0;
272:   PetscInfo(NULL, "HTTP result follows: \n%s\n", buff);
273:   return 0;
274: }

276: /*@C
277:       PetscHTTPSConnect - connect to a HTTPS server

279:     Input Parameters:
280: +    host - the name of the machine hosting the HTTPS server
281: .    port - the port number where the server is hosting, usually 443
282: -    ctx - value obtained with `PetscSSLInitializeContext()`

284:     Output Parameters:
285: +    sock - socket to connect
286: -    ssl - the argument passed to `PetscHTTPSRequest()`

288:     Level: advanced

290: .seealso: `PetscOpenSocket()`, `PetscHTTPSRequest()`, `PetscSSLInitializeContext()`
291: @*/
292: PetscErrorCode PetscHTTPSConnect(const char host[], int port, SSL_CTX *ctx, int *sock, SSL **ssl)
293: {
294:   BIO *sbio;

296:   /* Connect the TCP socket*/
297:   PetscOpenSocket(host, port, sock);

299:   /* Connect the SSL socket */
300:   *ssl = SSL_new(ctx);
301:   sbio = BIO_new_socket(*sock, BIO_NOCLOSE);
302:   SSL_set_bio(*ssl, sbio, sbio);
304:   return 0;
305: }

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

310:     Input Parameters:
311: +    buff - the char array containing the possible values
312: .    key - the key of the requested value
313: -    valuelen - the length of the array to contain the value associated with the key

315:     Output Parameters:
316: +    value - the value obtained
317: -    found - flag indicating if the value was found in the buff

319:     Level: advanced

321: .seealso: `PetscOpenSocket()`, `PetscHTTPSRequest()`, `PetscSSLInitializeContext()`, `PetscPushJSONValue()`
322: @*/
323: PetscErrorCode PetscPullJSONValue(const char buff[], const char key[], char value[], size_t valuelen, PetscBool *found)
324: {
325:   char  *v, *w;
326:   char   work[256];
327:   size_t len;

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

361: #include <ctype.h>

363: /*@C
364:     PetscPushJSONValue -  Puts a "key" : "value" pair onto a string

366:     Input Parameters:
367: +   buffer - the char array where the value will be put
368: .   key - the key value to be set
369: .   value - the value associated with the key
370: -   bufflen - the size of the buffer (currently ignored)

372:     Level: advanced

374:     Note:
375:     Ignores lengths so can cause buffer overflow

377: .seealso: `PetscOpenSocket()`, `PetscHTTPSRequest()`, `PetscSSLInitializeContext()`, `PetscPullJSONValue()`
378: @*/
379: PetscErrorCode PetscPushJSONValue(char buff[], const char key[], const char value[], size_t bufflen)
380: {
381:   size_t    len;
382:   PetscBool special;

384:   PetscStrcmp(value, "null", &special);
385:   if (!special) PetscStrcmp(value, "true", &special);
386:   if (!special) PetscStrcmp(value, "false", &special);
387:   if (!special) {
388:     PetscInt i;

390:     PetscStrlen(value, &len);
391:     special = PETSC_TRUE;
392:     for (i = 0; i < (int)len; i++) {
393:       if (!isdigit(value[i])) {
394:         special = PETSC_FALSE;
395:         break;
396:       }
397:     }
398:   }

400:   PetscStrcat(buff, "\"");
401:   PetscStrcat(buff, key);
402:   PetscStrcat(buff, "\":");
403:   if (!special) PetscStrcat(buff, "\"");
404:   PetscStrcat(buff, value);
405:   if (!special) PetscStrcat(buff, "\"");
406:   return 0;
407: }