xref: /petsc/src/sys/webclient/client.c (revision 041022618293821dae82bc1c7413cd3666530fdf)
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 $    ./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_METHOD     *meth;
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     meth = SSLv23_method();
56     ctx  = SSL_CTX_new(meth);
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   int            r;
97   size_t         request_len,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 = NULL;
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 
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,request_len);
179   switch (SSL_get_error(ssl,r)){
180     case SSL_ERROR_NONE:
181       if (request_len != 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, assuming  that it's terminated by a close */
188   r = SSL_read(ssl,buff,(int)buffsize);
189   len = r;
190   switch (SSL_get_error(ssl,r)){
191   case SSL_ERROR_NONE:
192     break;
193   case SSL_ERROR_ZERO_RETURN:
194     SSL_shutdown(ssl);  /* ignore shutdown error message */
195     break;
196   case SSL_ERROR_SYSCALL:
197     break;
198   default:
199     SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL read problem");
200   }
201   buff[len] = 0; /* null terminate string */
202   ierr = PetscInfo1(NULL,"HTTPS result follows: \n%s\n",buff);CHKERRQ(ierr);
203 
204   SSL_free(ssl);
205   ierr = PetscFree(request);CHKERRQ(ierr);
206   PetscFunctionReturn(0);
207 }
208 
209 #undef __FUNCT__
210 #define __FUNCT__ "PetscHTTPRequest"
211 /*
212      PetscHTTPRequest - Send a request to an HTTP server
213 
214    Input Parameters:
215 +   type - either "POST" or "GET"
216 .   url -  URL of request host/path
217 .   header - additional header information, may be NULL
218 .   ctype - data type of body, for example application/json
219 .   body - data to send to server
220 .   sock - obtained with PetscOpenSocket()
221 -   buffsize - size of buffer
222 
223    Output Parameter:
224 .   buff - everything returned from server
225  */
226 PetscErrorCode PetscHTTPRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],int sock,char buff[],size_t buffsize)
227 {
228   char           *request;
229   size_t         request_len;
230   PetscErrorCode ierr;
231 
232   PetscFunctionBegin;
233   ierr = PetscHTTPBuildRequest(type,url,header,ctype,body,&request);CHKERRQ(ierr);
234   ierr = PetscStrlen(request,&request_len);CHKERRQ(ierr);
235 
236   ierr = PetscBinaryWrite(sock,request,request_len,PETSC_CHAR,PETSC_FALSE);CHKERRQ(ierr);
237   ierr = PetscFree(request);CHKERRQ(ierr);
238   PetscBinaryRead(sock,buff,buffsize,PETSC_CHAR);
239   buff[buffsize-1] = 0;
240   ierr = PetscInfo1(NULL,"HTTP result follows: \n%s\n",buff);CHKERRQ(ierr);
241   PetscFunctionReturn(0);
242 }
243 
244 
245 #undef __FUNCT__
246 #define __FUNCT__ "PetscHTTPSConnect"
247 PetscErrorCode PetscHTTPSConnect(const char host[],int port,SSL_CTX *ctx,int *sock,SSL **ssl)
248 {
249   BIO            *sbio;
250   PetscErrorCode ierr;
251 
252   PetscFunctionBegin;
253   /* Connect the TCP socket*/
254   ierr = PetscOpenSocket(host,port,sock);CHKERRQ(ierr);
255 
256   /* Connect the SSL socket */
257   *ssl = SSL_new(ctx);
258   sbio = BIO_new_socket(*sock,BIO_NOCLOSE);
259   SSL_set_bio(*ssl,sbio,sbio);
260   if (SSL_connect(*ssl) <= 0) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL connect error");
261   PetscFunctionReturn(0);
262 }
263 
264