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