Actual source code: client.c
petsc-3.13.6 2020-09-29
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: }