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