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