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