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