xref: /petsc/src/sys/webclient/client.c (revision 536b137f95b6f40d8df0141fed3efeb26b0ec7af)
1 
2 #include <petscwebclient.h>
3 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
4 #pragma gcc diagnostic ignored "-Wdeprecated-declarations"
5 
6 static BIO *bio_err = NULL;
7 
8 #define PASSWORD "password"
9 
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
18 
19 static void sigpipe_handle(int x)
20 {
21 }
22 
23 #undef __FUNCT__
24 #define __FUNCT__ "PetscSSLInitializeContext"
25 /*
26     PetscSSLInitializeContext - Set up an SSL context suitable for initiating HTTPS requests.
27 
28     If built with PETSC_USE_SSL_CERTIFICATE requires the user have created a self-signed certificate with
29 
30 $    saws/CA.pl  -newcert  (using the passphrase of password)
31 $    cat newkey.pem newcert.pem > sslclient.pem
32 
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.
35 
36 */
37 PetscErrorCode PetscSSLInitializeContext(SSL_CTX **octx)
38 {
39     SSL_CTX        *ctx;
40 #if defined(PETSC_USE_SSL_CERTIFICATE)
41     char           keyfile[PETSC_MAX_PATH_LEN];
42     PetscBool      exists;
43     PetscErrorCode ierr;
44 #endif
45 
46     PetscFunctionBegin;
47     if (!bio_err){
48       SSL_library_init();
49       SSL_load_error_strings();
50       bio_err = BIO_new_fp(stderr,BIO_NOCLOSE);
51     }
52 
53     /* Set up a SIGPIPE handler */
54     signal(SIGPIPE,sigpipe_handle);
55 
56     ctx  = SSL_CTX_new(SSLv23_method());
57     SSL_CTX_set_mode(ctx,SSL_MODE_AUTO_RETRY);
58 
59 #if defined(PETSC_USE_SSL_CERTIFICATE)
60     /* Locate keyfile */
61     ierr = PetscStrcpy(keyfile,"sslclient.pem");CHKERRQ(ierr);
62     ierr = PetscTestFile(keyfile,'r',&exists);CHKERRQ(ierr);
63     if (!exists) {
64       ierr = PetscGetHomeDirectory(keyfile,PETSC_MAX_PATH_LEN);CHKERRQ(ierr);
65       ierr = PetscStrcat(keyfile,"/");CHKERRQ(ierr);
66       ierr = PetscStrcat(keyfile,"sslclient.pem");CHKERRQ(ierr);
67       ierr = PetscTestFile(keyfile,'r',&exists);CHKERRQ(ierr);
68       if (!exists) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to locate sslclient.pem file in current directory or home directory");
69     }
70 
71     /* Load our keys and certificates*/
72     if (!(SSL_CTX_use_certificate_chain_file(ctx,keyfile))) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Cannot read certificate file");
73 
74     SSL_CTX_set_default_passwd_cb(ctx,password_cb);
75     if (!(SSL_CTX_use_PrivateKey_file(ctx,keyfile,SSL_FILETYPE_PEM))) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Cannot read key file");
76 #endif
77 
78     *octx = ctx;
79     PetscFunctionReturn(0);
80 }
81 
82 #undef __FUNCT__
83 #define __FUNCT__ "PetscSSLDestroyContext"
84 PetscErrorCode PetscSSLDestroyContext(SSL_CTX *ctx)
85 {
86   PetscFunctionBegin;
87   SSL_CTX_free(ctx);
88   PetscFunctionReturn(0);
89 }
90 
91 #undef __FUNCT__
92 #define __FUNCT__ "PetscHTTPBuildRequest"
93 PetscErrorCode PetscHTTPBuildRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],char **outrequest)
94 {
95   char           *request=0;
96   char           contentlength[40],contenttype[80],*path,*host;
97   size_t         request_len,headlen,bodylen,contentlen,pathlen,hostlen,typelen,contenttypelen = 0;
98   PetscErrorCode ierr;
99   PetscBool      flg;
100 
101   PetscFunctionBegin;
102   ierr = PetscStrallocpy(url,&host);CHKERRQ(ierr);
103   ierr = PetscStrchr(host,'/',&path);CHKERRQ(ierr);
104   if (!path) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE,"url must contain / it is %s",url);
105   *path = 0;
106   ierr  = PetscStrlen(host,&hostlen);CHKERRQ(ierr);
107 
108   ierr = PetscStrchr(url,'/',&path);CHKERRQ(ierr);
109   ierr = PetscStrlen(path,&pathlen);CHKERRQ(ierr);
110 
111   if (header) {
112     ierr = PetscStrendswith(header,"\r\n",&flg);CHKERRQ(ierr);
113     if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"header must end with \\r\\n");
114   }
115 
116   ierr = PetscStrlen(type,&typelen);CHKERRQ(ierr);
117   if (ctype) {
118     ierr = PetscSNPrintf(contenttype,80,"Content-Type: %s\r\n",ctype);CHKERRQ(ierr);
119     ierr = PetscStrlen(contenttype,&contenttypelen);CHKERRQ(ierr);
120   }
121   ierr = PetscStrlen(header,&headlen);CHKERRQ(ierr);
122   ierr = PetscStrlen(body,&bodylen);CHKERRQ(ierr);
123   ierr = PetscSNPrintf(contentlength,40,"Content-Length: %d\r\n\r\n",(int)bodylen);CHKERRQ(ierr);
124   ierr = PetscStrlen(contentlength,&contentlen);CHKERRQ(ierr);
125 
126   /* Now construct our HTTP request */
127   request_len = typelen + 1 + pathlen + hostlen + 100 + headlen + contenttypelen + contentlen + bodylen + 1;
128   ierr = PetscMalloc1(request_len,&request);CHKERRQ(ierr);
129   ierr = PetscStrcpy(request,type);CHKERRQ(ierr);
130   ierr = PetscStrcat(request," ");CHKERRQ(ierr);
131   ierr = PetscStrcat(request,path);CHKERRQ(ierr);
132   ierr = PetscStrcat(request," HTTP/1.1\r\nHost: ");CHKERRQ(ierr);
133   ierr = PetscStrcat(request,host);CHKERRQ(ierr);
134   ierr = PetscFree(host);CHKERRQ(ierr);
135   ierr = PetscStrcat(request,"\r\nUser-Agent:PETScClient\r\n");CHKERRQ(ierr);
136   ierr = PetscStrcat(request,header);CHKERRQ(ierr);
137   if (ctype) {
138     ierr = PetscStrcat(request,contenttype);CHKERRQ(ierr);
139   }
140   ierr = PetscStrcat(request,contentlength);CHKERRQ(ierr);
141   ierr = PetscStrcat(request,body);CHKERRQ(ierr);
142   ierr = PetscStrlen(request,&request_len);CHKERRQ(ierr);
143   ierr = PetscInfo1(NULL,"HTTPS request follows: \n%s\n",request);CHKERRQ(ierr);
144 
145   *outrequest = request;
146   PetscFunctionReturn(0);
147 }
148 
149 
150 #undef __FUNCT__
151 #define __FUNCT__ "PetscHTTPSRequest"
152 /*
153      PetscHTTPSRequest - Send a request to an HTTPS server
154 
155    Input Parameters:
156 +   type - either "POST" or "GET"
157 .   url -  URL of request host/path
158 .   header - additional header information, may be NULL
159 .   ctype - data type of body, for example application/json
160 .   body - data to send to server
161 .   ssl - obtained with PetscHTTPSConnect()
162 -   buffsize - size of buffer
163 
164    Output Parameter:
165 .   buff - everything returned from server
166  */
167 PetscErrorCode PetscHTTPSRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],SSL *ssl,char buff[],size_t buffsize)
168 {
169   char           *request;
170   int            r;
171   size_t         request_len,len;
172   PetscErrorCode ierr;
173   PetscBool      foundbody = PETSC_FALSE;
174 
175   PetscFunctionBegin;
176   ierr = PetscHTTPBuildRequest(type,url,header,ctype,body,&request);CHKERRQ(ierr);
177   ierr = PetscStrlen(request,&request_len);CHKERRQ(ierr);
178 
179   r = SSL_write(ssl,request,(int)request_len);
180   switch (SSL_get_error(ssl,r)){
181     case SSL_ERROR_NONE:
182       if (request_len != (size_t)r) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Incomplete write to SSL socket");
183       break;
184     default:
185       SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL socket write problem");
186   }
187 
188   /* Now read the server's response, globus sends it in two chunks hence must read a second time if needed */
189   ierr      = PetscMemzero(buff,buffsize);CHKERRQ(ierr);
190   len       = 0;
191   foundbody = PETSC_FALSE;
192   do {
193     char   *clen;
194     int    cl;
195     size_t nlen;
196 
197     r = SSL_read(ssl,buff+len,(int)buffsize);
198     len += r;
199     switch (SSL_get_error(ssl,r)){
200     case SSL_ERROR_NONE:
201       break;
202     case SSL_ERROR_ZERO_RETURN:
203       foundbody = PETSC_TRUE;
204       SSL_shutdown(ssl);
205       break;
206     case SSL_ERROR_SYSCALL:
207       foundbody = PETSC_TRUE;
208       break;
209     default:
210       SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL read problem");
211     }
212 
213     ierr = PetscStrstr(buff,"Content-Length: ",&clen);CHKERRQ(ierr);
214     if (clen) {
215       clen += 15;
216       sscanf(clen,"%d",&cl);
217       if (!cl) foundbody = PETSC_TRUE;
218       else {
219         ierr = PetscStrstr(buff,"\r\n\r\n",&clen);CHKERRQ(ierr);
220         if (clen) {
221           ierr = PetscStrlen(clen,&nlen);CHKERRQ(ierr);
222           if (nlen-4 == (size_t) cl) foundbody = PETSC_TRUE;
223         }
224       }
225     } else {
226       /* if no content length than must leave because you don't know if you can read again */
227       foundbody = PETSC_TRUE;
228     }
229   } while (!foundbody);
230   ierr = PetscInfo1(NULL,"HTTPS result follows: \n%s\n",buff);CHKERRQ(ierr);
231 
232   SSL_free(ssl);
233   ierr = PetscFree(request);CHKERRQ(ierr);
234   PetscFunctionReturn(0);
235 }
236 
237 #undef __FUNCT__
238 #define __FUNCT__ "PetscHTTPRequest"
239 /*
240      PetscHTTPRequest - Send a request to an HTTP server
241 
242    Input Parameters:
243 +   type - either "POST" or "GET"
244 .   url -  URL of request host/path
245 .   header - additional header information, may be NULL
246 .   ctype - data type of body, for example application/json
247 .   body - data to send to server
248 .   sock - obtained with PetscOpenSocket()
249 -   buffsize - size of buffer
250 
251    Output Parameter:
252 .   buff - everything returned from server
253  */
254 PetscErrorCode PetscHTTPRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],int sock,char buff[],size_t buffsize)
255 {
256   char           *request;
257   size_t         request_len;
258   PetscErrorCode ierr;
259 
260   PetscFunctionBegin;
261   ierr = PetscHTTPBuildRequest(type,url,header,ctype,body,&request);CHKERRQ(ierr);
262   ierr = PetscStrlen(request,&request_len);CHKERRQ(ierr);
263 
264   ierr = PetscBinaryWrite(sock,request,request_len,PETSC_CHAR,PETSC_FALSE);CHKERRQ(ierr);
265   ierr = PetscFree(request);CHKERRQ(ierr);
266   PetscBinaryRead(sock,buff,buffsize,PETSC_CHAR);
267   buff[buffsize-1] = 0;
268   ierr = PetscInfo1(NULL,"HTTP result follows: \n%s\n",buff);CHKERRQ(ierr);
269   PetscFunctionReturn(0);
270 }
271 
272 #undef __FUNCT__
273 #define __FUNCT__ "PetscHTTPSConnect"
274 PetscErrorCode PetscHTTPSConnect(const char host[],int port,SSL_CTX *ctx,int *sock,SSL **ssl)
275 {
276   BIO            *sbio;
277   PetscErrorCode ierr;
278 
279   PetscFunctionBegin;
280   /* Connect the TCP socket*/
281   ierr = PetscOpenSocket(host,port,sock);CHKERRQ(ierr);
282 
283   /* Connect the SSL socket */
284   *ssl = SSL_new(ctx);
285   sbio = BIO_new_socket(*sock,BIO_NOCLOSE);
286   SSL_set_bio(*ssl,sbio,sbio);
287   if (SSL_connect(*ssl) <= 0) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL connect error");
288   PetscFunctionReturn(0);
289 }
290 
291 #undef __FUNCT__
292 #define __FUNCT__ "PetscPullJSONValue"
293 /*
294      Given a JSON response containing the substring with "key" : "value"  where there may or not be spaces around the : returns the value.
295 */
296 PetscErrorCode PetscPullJSONValue(const char buff[],const char key[],char value[],size_t valuelen,PetscBool *found)
297 {
298   PetscErrorCode ierr;
299   char           *v,*w;
300   char           work[256];
301   size_t         len;
302 
303   PetscFunctionBegin;
304   ierr = PetscStrcpy(work,"\"");CHKERRQ(ierr);
305   ierr = PetscStrncat(work,key,250);CHKERRQ(ierr);
306   ierr = PetscStrcat(work,"\":");CHKERRQ(ierr);
307   ierr = PetscStrstr(buff,work,&v);CHKERRQ(ierr);
308   ierr = PetscStrlen(work,&len);CHKERRQ(ierr);
309   if (v) {
310     v += len;
311   } else {
312     work[len++-1] = 0;
313     ierr = PetscStrcat(work," :");CHKERRQ(ierr);
314     ierr = PetscStrstr(buff,work,&v);CHKERRQ(ierr);
315     if (!v) {
316       *found = PETSC_FALSE;
317       PetscFunctionReturn(0);
318     }
319     v += len;
320   }
321   ierr = PetscStrchr(v,'\"',&v);CHKERRQ(ierr);
322   if (!v) {
323     *found = PETSC_FALSE;
324     PetscFunctionReturn(0);
325   }
326   ierr = PetscStrchr(v+1,'\"',&w);CHKERRQ(ierr);
327   if (!w) {
328     *found = PETSC_FALSE;
329     PetscFunctionReturn(0);
330   }
331   *found = PETSC_TRUE;
332   ierr = PetscStrncpy(value,v+1,PetscMin((size_t)(w-v),valuelen));CHKERRQ(ierr);
333   PetscFunctionReturn(0);
334 }
335 
336 #include <ctype.h>
337 
338 #undef __FUNCT__
339 #define __FUNCT__ "PetscPushJSONValue"
340 /*
341      Pushs a "key" : "value" pair onto a string
342 
343      Ignores lengths so can cause buffer overflow
344 */
345 PetscErrorCode PetscPushJSONValue(char buff[],const char key[],const char value[],size_t bufflen)
346 {
347   PetscErrorCode ierr;
348   size_t         len;
349   PetscBool      special;
350 
351   PetscFunctionBegin;
352   ierr = PetscStrcmp(value,"null",&special);CHKERRQ(ierr);
353   if (!special) {
354     ierr = PetscStrcmp(value,"true",&special);CHKERRQ(ierr);
355   }
356   if (!special) {
357     ierr = PetscStrcmp(value,"false",&special);CHKERRQ(ierr);
358   }
359   if (!special) {
360     PetscInt i;
361 
362     ierr    = PetscStrlen(value,&len);CHKERRQ(ierr);
363     special = PETSC_TRUE;
364     for (i=0; i<(int)len; i++) {
365       if (!isdigit(value[i])) {
366         special = PETSC_FALSE;
367         break;
368       }
369     }
370   }
371 
372   ierr = PetscStrcat(buff,"\"");CHKERRQ(ierr);
373   ierr = PetscStrcat(buff,key);CHKERRQ(ierr);
374   ierr = PetscStrcat(buff,"\":");CHKERRQ(ierr);
375   if (!special) {
376     ierr = PetscStrcat(buff,"\"");CHKERRQ(ierr);
377   }
378   ierr = PetscStrcat(buff,value);CHKERRQ(ierr);
379   if (!special) {
380     ierr = PetscStrcat(buff,"\"");CHKERRQ(ierr);
381   }
382   PetscFunctionReturn(0);
383 }
384