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