xref: /petsc/src/sys/webclient/client.c (revision 7a3410ede202c5dd1a2e9ef229f2be7d9c36a0b5)
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_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   ierr = PetscStrlen(host,&hostlen);CHKERRQ(ierr);
103 
104   ierr = PetscStrchr(url,'/',&path);CHKERRQ(ierr);
105   ierr = PetscStrlen(path,&pathlen);CHKERRQ(ierr);
106 
107   if (header) {
108     ierr = PetscStrendswith(header,"\r\n",&flg);CHKERRQ(ierr);
109     if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"header must end with \\r\\n");
110   }
111 
112   ierr = PetscStrlen(type,&typelen);CHKERRQ(ierr);
113   if (ctype) {
114     ierr = PetscSNPrintf(contenttype,80,"Content-Type: %s\r\n",ctype);CHKERRQ(ierr);
115     ierr = PetscStrlen(contenttype,&contenttypelen);CHKERRQ(ierr);
116   }
117   ierr = PetscStrlen(header,&headlen);CHKERRQ(ierr);
118   ierr = PetscStrlen(body,&bodylen);CHKERRQ(ierr);
119   ierr = PetscSNPrintf(contentlength,40,"Content-Length: %d\r\n\r\n",(int)bodylen);CHKERRQ(ierr);
120   ierr = PetscStrlen(contentlength,&contentlen);CHKERRQ(ierr);
121 
122   /* Now construct our HTTP request */
123   request_len = typelen + 1 + pathlen + hostlen + 100 + headlen + contenttypelen + contentlen + bodylen + 1;
124   ierr = PetscMalloc1(request_len,&request);CHKERRQ(ierr);
125   ierr = PetscStrcpy(request,type);CHKERRQ(ierr);
126   ierr = PetscStrcat(request," ");CHKERRQ(ierr);
127   ierr = PetscStrcat(request,path);CHKERRQ(ierr);
128   ierr = PetscStrcat(request," HTTP/1.1\r\nHost: ");CHKERRQ(ierr);
129   ierr = PetscStrcat(request,host);CHKERRQ(ierr);
130   ierr = PetscFree(host);CHKERRQ(ierr);
131   ierr = PetscStrcat(request,"\r\nUser-Agent:PETScClient\r\n");CHKERRQ(ierr);
132   ierr = PetscStrcat(request,header);CHKERRQ(ierr);
133   if (ctype) {
134     ierr = PetscStrcat(request,contenttype);CHKERRQ(ierr);
135   }
136   ierr = PetscStrcat(request,contentlength);CHKERRQ(ierr);
137   ierr = PetscStrcat(request,body);CHKERRQ(ierr);
138   ierr = PetscStrlen(request,&request_len);CHKERRQ(ierr);
139   ierr = PetscInfo1(NULL,"HTTPS request follows: \n%s\n",request);CHKERRQ(ierr);
140 
141   *outrequest = request;
142   PetscFunctionReturn(0);
143 }
144 
145 
146 #undef __FUNCT__
147 #define __FUNCT__ "PetscHTTPSRequest"
148 /*
149      PetscHTTPSRequest - Send a request to an HTTPS server
150 
151    Input Parameters:
152 +   type - either "POST" or "GET"
153 .   url -  URL of request host/path
154 .   header - additional header information, may be NULL
155 .   ctype - data type of body, for example application/json
156 .   body - data to send to server
157 .   ssl - obtained with PetscHTTPSConnect()
158 -   buffsize - size of buffer
159 
160    Output Parameter:
161 .   buff - everything returned from server
162  */
163 PetscErrorCode PetscHTTPSRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],SSL *ssl,char buff[],size_t buffsize)
164 {
165   char           *request;
166   int            r;
167   size_t         request_len,len;
168   PetscErrorCode ierr;
169 
170   PetscFunctionBegin;
171   ierr = PetscHTTPBuildRequest(type,url,header,ctype,body,&request);CHKERRQ(ierr);
172   ierr = PetscStrlen(request,&request_len);CHKERRQ(ierr);
173 
174   r = SSL_write(ssl,request,(int)request_len);
175   switch (SSL_get_error(ssl,r)){
176     case SSL_ERROR_NONE:
177       if (request_len != (size_t)r) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Incomplete write to SSL socket");
178       break;
179     default:
180       SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL socket write problem");
181   }
182 
183   /* Now read the server's response, assuming  that it's terminated by a close */
184   r = SSL_read(ssl,buff,(int)buffsize);
185   len = r;
186   switch (SSL_get_error(ssl,r)){
187   case SSL_ERROR_NONE:
188     break;
189   case SSL_ERROR_ZERO_RETURN:
190     SSL_shutdown(ssl);  /* ignore shutdown error message */
191     break;
192   case SSL_ERROR_SYSCALL:
193     break;
194   default:
195     SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"SSL read problem");
196   }
197   buff[len] = 0; /* null terminate string */
198   ierr = PetscInfo1(NULL,"HTTPS result follows: \n%s\n",buff);CHKERRQ(ierr);
199 
200   SSL_free(ssl);
201   ierr = PetscFree(request);CHKERRQ(ierr);
202   PetscFunctionReturn(0);
203 }
204 
205 #undef __FUNCT__
206 #define __FUNCT__ "PetscHTTPRequest"
207 /*
208      PetscHTTPRequest - Send a request to an HTTP server
209 
210    Input Parameters:
211 +   type - either "POST" or "GET"
212 .   url -  URL of request host/path
213 .   header - additional header information, may be NULL
214 .   ctype - data type of body, for example application/json
215 .   body - data to send to server
216 .   sock - obtained with PetscOpenSocket()
217 -   buffsize - size of buffer
218 
219    Output Parameter:
220 .   buff - everything returned from server
221  */
222 PetscErrorCode PetscHTTPRequest(const char type[],const char url[],const char header[],const char ctype[],const char body[],int sock,char buff[],size_t buffsize)
223 {
224   char           *request;
225   size_t         request_len;
226   PetscErrorCode ierr;
227 
228   PetscFunctionBegin;
229   ierr = PetscHTTPBuildRequest(type,url,header,ctype,body,&request);CHKERRQ(ierr);
230   ierr = PetscStrlen(request,&request_len);CHKERRQ(ierr);
231 
232   ierr = PetscBinaryWrite(sock,request,request_len,PETSC_CHAR,PETSC_FALSE);CHKERRQ(ierr);
233   ierr = PetscFree(request);CHKERRQ(ierr);
234   PetscBinaryRead(sock,buff,buffsize,PETSC_CHAR);
235   buff[buffsize-1] = 0;
236   ierr = PetscInfo1(NULL,"HTTP result follows: \n%s\n",buff);CHKERRQ(ierr);
237   PetscFunctionReturn(0);
238 }
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