xref: /petsc/src/sys/webclient/client.c (revision 68e695932dc3ba76353bc2baf983104ab033ae17)
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 
56 #if defined(PETSC_USE_SSL_CERTIFICATE)
57     /* Locate keyfile */
58     ierr = PetscStrcpy(keyfile,"sslclient.pem");CHKERRQ(ierr);
59     ierr = PetscTestFile(keyfile,'r',&exists);CHKERRQ(ierr);
60     if (!exists) {
61       ierr = PetscGetHomeDirectory(keyfile,PETSC_MAX_PATH_LEN);CHKERRQ(ierr);
62       ierr = PetscStrcat(keyfile,"/");CHKERRQ(ierr);
63       ierr = PetscStrcat(keyfile,"sslclient.pem");CHKERRQ(ierr);
64       ierr = PetscTestFile(keyfile,'r',&exists);CHKERRQ(ierr);
65       if (!exists) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Unable to locate sslclient.pem file in current directory or home directory");
66     }
67 
68     /* Load our keys and certificates*/
69     if (!(SSL_CTX_use_certificate_chain_file(ctx,keyfile))) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Cannot read certificate file");
70 
71     SSL_CTX_set_default_passwd_cb(ctx,password_cb);
72     if (!(SSL_CTX_use_PrivateKey_file(ctx,keyfile,SSL_FILETYPE_PEM))) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FILE_OPEN,"Cannot read key file");
73 #endif
74 
75     *octx = ctx;
76     PetscFunctionReturn(0);
77 }
78 
79 #undef __FUNCT__
80 #define __FUNCT__ "PetscSSLDestroyContext"
81 PetscErrorCode PetscSSLDestroyContext(SSL_CTX *ctx)
82 {
83   PetscFunctionBegin;
84   SSL_CTX_free(ctx);
85   PetscFunctionReturn(0);
86 }
87 
88 #undef __FUNCT__
89 #define __FUNCT__ "PetscHTTPBuildRequest"
90 PetscErrorCode PetscHTTPBuildRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],char **outrequest)
91 {
92   char           *request=0;
93   char           contentlength[40],contenttype[80],*path,*host;
94   size_t         request_len,headlen,bodylen,contentlen,pathlen,hostlen,typelen,contenttypelen = 0;
95   PetscErrorCode ierr;
96   PetscBool      flg;
97 
98   PetscFunctionBegin;
99   ierr = PetscStrallocpy(url,&host);CHKERRQ(ierr);
100   ierr = PetscStrchr(host,'/',&path);CHKERRQ(ierr);
101   if (!path) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE,"url must contain / it is %s",url);
102   *path = 0;
103   ierr  = PetscStrlen(host,&hostlen);CHKERRQ(ierr);
104 
105   ierr = PetscStrchr(url,'/',&path);CHKERRQ(ierr);
106   ierr = PetscStrlen(path,&pathlen);CHKERRQ(ierr);
107 
108   if (header) {
109     ierr = PetscStrendswith(header,"\r\n",&flg);CHKERRQ(ierr);
110     if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"header must end with \\r\\n");
111   }
112 
113   ierr = PetscStrlen(type,&typelen);CHKERRQ(ierr);
114   if (ctype) {
115     ierr = PetscSNPrintf(contenttype,80,"Content-Type: %s\r\n",ctype);CHKERRQ(ierr);
116     ierr = PetscStrlen(contenttype,&contenttypelen);CHKERRQ(ierr);
117   }
118   ierr = PetscStrlen(header,&headlen);CHKERRQ(ierr);
119   ierr = PetscStrlen(body,&bodylen);CHKERRQ(ierr);
120   ierr = PetscSNPrintf(contentlength,40,"Content-Length: %d\r\n\r\n",(int)bodylen);CHKERRQ(ierr);
121   ierr = PetscStrlen(contentlength,&contentlen);CHKERRQ(ierr);
122 
123   /* Now construct our HTTP request */
124   request_len = typelen + 1 + pathlen + hostlen + 100 + headlen + contenttypelen + contentlen + bodylen + 1;
125   ierr = PetscMalloc1(request_len,&request);CHKERRQ(ierr);
126   ierr = PetscStrcpy(request,type);CHKERRQ(ierr);
127   ierr = PetscStrcat(request," ");CHKERRQ(ierr);
128   ierr = PetscStrcat(request,path);CHKERRQ(ierr);
129   ierr = PetscStrcat(request," HTTP/1.1\r\nHost: ");CHKERRQ(ierr);
130   ierr = PetscStrcat(request,host);CHKERRQ(ierr);
131   ierr = PetscFree(host);CHKERRQ(ierr);
132   ierr = PetscStrcat(request,"\r\nUser-Agent:PETScClient\r\n");CHKERRQ(ierr);
133   ierr = PetscStrcat(request,header);CHKERRQ(ierr);
134   if (ctype) {
135     ierr = PetscStrcat(request,contenttype);CHKERRQ(ierr);
136   }
137   ierr = PetscStrcat(request,contentlength);CHKERRQ(ierr);
138   ierr = PetscStrcat(request,body);CHKERRQ(ierr);
139   ierr = PetscStrlen(request,&request_len);CHKERRQ(ierr);
140   ierr = PetscInfo1(NULL,"HTTPS request follows: \n%s\n",request);CHKERRQ(ierr);
141 
142   *outrequest = request;
143   PetscFunctionReturn(0);
144 }
145 
146 
147 #undef __FUNCT__
148 #define __FUNCT__ "PetscHTTPSRequest"
149 /*
150      PetscHTTPSRequest - Send a request to an HTTPS server
151 
152    Input Parameters:
153 +   type - either "POST" or "GET"
154 .   url -  URL of request host/path
155 .   header - additional header information, may be NULL
156 .   ctype - data type of body, for example application/json
157 .   body - data to send to server
158 .   ssl - obtained with PetscHTTPSConnect()
159 -   buffsize - size of buffer
160 
161    Output Parameter:
162 .   buff - everything returned from server
163  */
164 PetscErrorCode PetscHTTPSRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],SSL *ssl,char buff[],size_t buffsize)
165 {
166   char           *request;
167   int            r;
168   size_t         request_len,len;
169   PetscErrorCode ierr;
170 
171   PetscFunctionBegin;
172   ierr = PetscHTTPBuildRequest(type,url,header,ctype,body,&request);CHKERRQ(ierr);
173   ierr = PetscStrlen(request,&request_len);CHKERRQ(ierr);
174 
175   r = SSL_write(ssl,request,(int)request_len);
176   switch (SSL_get_error(ssl,r)){
177     case SSL_ERROR_NONE:
178       if (request_len != (size_t)r) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Incomplete write to SSL socket");
179       break;
180     default:
181       SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL socket write problem");
182   }
183 
184   /* Now read the server's response, assuming  that it's terminated by a close */
185   r = SSL_read(ssl,buff,(int)buffsize);
186   len = r;
187   switch (SSL_get_error(ssl,r)){
188   case SSL_ERROR_NONE:
189     break;
190   case SSL_ERROR_ZERO_RETURN:
191     SSL_shutdown(ssl);  /* ignore shutdown error message */
192     break;
193   case SSL_ERROR_SYSCALL:
194     break;
195   default:
196     SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL read problem");
197   }
198   buff[len] = 0; /* null terminate string */
199   ierr = PetscInfo1(NULL,"HTTPS result follows: \n%s\n",buff);CHKERRQ(ierr);
200 
201   SSL_free(ssl);
202   ierr = PetscFree(request);CHKERRQ(ierr);
203   PetscFunctionReturn(0);
204 }
205 
206 #undef __FUNCT__
207 #define __FUNCT__ "PetscHTTPRequest"
208 /*
209      PetscHTTPRequest - Send a request to an HTTP server
210 
211    Input Parameters:
212 +   type - either "POST" or "GET"
213 .   url -  URL of request host/path
214 .   header - additional header information, may be NULL
215 .   ctype - data type of body, for example application/json
216 .   body - data to send to server
217 .   sock - obtained with PetscOpenSocket()
218 -   buffsize - size of buffer
219 
220    Output Parameter:
221 .   buff - everything returned from server
222  */
223 PetscErrorCode PetscHTTPRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],int sock,char buff[],size_t buffsize)
224 {
225   char           *request;
226   size_t         request_len;
227   PetscErrorCode ierr;
228 
229   PetscFunctionBegin;
230   ierr = PetscHTTPBuildRequest(type,url,header,ctype,body,&request);CHKERRQ(ierr);
231   ierr = PetscStrlen(request,&request_len);CHKERRQ(ierr);
232 
233   ierr = PetscBinaryWrite(sock,request,request_len,PETSC_CHAR,PETSC_FALSE);CHKERRQ(ierr);
234   ierr = PetscFree(request);CHKERRQ(ierr);
235   PetscBinaryRead(sock,buff,buffsize,PETSC_CHAR);
236   buff[buffsize-1] = 0;
237   ierr = PetscInfo1(NULL,"HTTP result follows: \n%s\n",buff);CHKERRQ(ierr);
238   PetscFunctionReturn(0);
239 }
240 
241 #undef __FUNCT__
242 #define __FUNCT__ "PetscHTTPSConnect"
243 PetscErrorCode PetscHTTPSConnect(const char host[],int port,SSL_CTX *ctx,int *sock,SSL **ssl)
244 {
245   BIO            *sbio;
246   PetscErrorCode ierr;
247 
248   PetscFunctionBegin;
249   /* Connect the TCP socket*/
250   ierr = PetscOpenSocket(host,port,sock);CHKERRQ(ierr);
251 
252   /* Connect the SSL socket */
253   *ssl = SSL_new(ctx);
254   sbio = BIO_new_socket(*sock,BIO_NOCLOSE);
255   SSL_set_bio(*ssl,sbio,sbio);
256   if (SSL_connect(*ssl) <= 0) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL connect error");
257   PetscFunctionReturn(0);
258 }
259 
260 #undef __FUNCT__
261 #define __FUNCT__ "PetscPullJSONValue"
262 /*
263      Given a JSON response containing the substring with "key" : "value"  where there may or not be spaces around the : returns the value.
264 */
265 PetscErrorCode PetscPullJSONValue(const char buff[],const char key[],char value[],size_t valuelen,PetscBool *found)
266 {
267   PetscErrorCode ierr;
268   char           *v,*w;
269   char           work[256];
270   size_t         len;
271 
272   PetscFunctionBegin;
273   ierr = PetscStrcpy(work,"\"");CHKERRQ(ierr);
274   ierr = PetscStrncat(work,key,250);CHKERRQ(ierr);
275   ierr = PetscStrcat(work,"\":");CHKERRQ(ierr);
276   ierr = PetscStrstr(buff,work,&v);CHKERRQ(ierr);
277   ierr = PetscStrlen(work,&len);CHKERRQ(ierr);
278   if (v) {
279     v += len;
280   } else {
281     work[len++-1] = 0;
282     ierr = PetscStrcat(work," :");CHKERRQ(ierr);
283     ierr = PetscStrstr(buff,work,&v);CHKERRQ(ierr);
284     if (!v) {
285       *found = PETSC_FALSE;
286       PetscFunctionReturn(0);
287     }
288     v += len;
289   }
290   ierr = PetscStrchr(v,'\"',&v);CHKERRQ(ierr);
291   if (!v) {
292     *found = PETSC_FALSE;
293     PetscFunctionReturn(0);
294   }
295   ierr = PetscStrchr(v+1,'\"',&w);CHKERRQ(ierr);
296   if (!w) {
297     *found = PETSC_FALSE;
298     PetscFunctionReturn(0);
299   }
300   *found = PETSC_TRUE;
301   ierr = PetscStrncpy(value,v+1,PetscMin(w-v,valuelen));CHKERRQ(ierr);
302   PetscFunctionReturn(0);
303 }
304