1 /*
2 * copied and amended from a simple http socket program:
3 * "Simple Internet client program - by Dan Drown <abob@linux.com>"
4 * available (last I looked) at:
5 * http://linux.omnipotent.net/article.php?article_id=5424
6 */
7
8 #include "mhttp.h"
9
10
11 #ifdef DOHERROR
herror(char * str)12 void herror(char *str){
13 fprintf(stderr, "herror: %s\n", str);
14 }
15 #endif
16
17 bool mhttp_lets_debug; /* global debugging flag */
18 bool mhttp_body_set_flag; /* global body set flag */
19 bool mhttp_host_hdr; /* I got a Host header */
20 int mhttp_protocol = 0;
21 bool mhttp_first_init = false;
22
23 int mhttp_hcnt,
24 mhttp_rcode,
25 mhttp_response_length;
26
27 char *mhttp_body,
28 *mhttp_response,
29 *mhttp_reason,
30 mhttp_resp_headers[MAX_HDR_STR],
31 *mhttp_headers[MAX_HEADERS],
32 *mhttp_buffers[MAX_BUFFERS];
33
34 mhttp_conn_t mhttp_connection = NULL;
35
36 mhttp_conn_t mhttp_last_connection = NULL;
37
38
39
mhttp_call(char * paction,char * purl)40 int mhttp_call(char *paction, char *purl)
41 {
42 bool found_hdrs; /* found the end of headers flag */
43 bool rcode_flag; /* found return code flag */
44 bool newcon_flag; /* new connection flag */
45 bool chunked; /* transfer encoding is chunked */
46 char *action, *url, *host, *ptr, *eomsg, *clptr;
47 char *req;
48 char *surl;
49 char *cert_str;
50 int port,
51 returnval,
52 len,
53 curr_len,
54 buffer_size,
55 this_chunk,
56 i,
57 rem,
58 pos;
59 char str[MAX_STR];
60
61
62 newcon_flag = false;
63 if (!mhttp_first_init)
64 mhttp_init();
65 memset(mhttp_resp_headers, 0, MAX_HDR_STR);
66
67 mhttp_connection = mhttp_new_conn();
68
69 if ((returnval = check_action(paction, &action)) < 0)
70 return returnval;
71
72 if ((returnval = check_url(purl, &url, &host)) < 0)
73 return returnval;
74
75 if ((port = get_port_and_uri(purl, host, &surl)) < 0)
76 return port;
77 mhttp_connection->host = strdup(host);
78 mhttp_connection->port = port;
79
80 mhttp_debug("action: %s host: %s port: %d url: %s", action, host, port, url);
81 mhttp_debug("purl is: #%s#", purl);
82 if (strncmp(purl, "https", 5) == 0)
83 mhttp_connection->is_ssl = true;
84
85 mhttp_debug("This connection: %s / %d / %d",
86 mhttp_connection->host,
87 mhttp_connection->port,
88 mhttp_connection->is_ssl);
89
90 if (mhttp_protocol == 1 && ! mhttp_host_hdr){
91 mhttp_debug("This is HTTP/1.1 and we don't have a Host header");
92 return -19;
93 }
94
95 if (mhttp_last_connection != NULL){
96 mhttp_debug("OLD connection: %s / %d / %d",
97 mhttp_last_connection->host,
98 mhttp_last_connection->port,
99 mhttp_last_connection->is_ssl);
100 }
101 #ifdef GOTSSL
102 if (mhttp_last_connection != NULL &&
103 strcmp(mhttp_connection->host, mhttp_last_connection->host) == 0 &&
104 mhttp_last_connection->port == mhttp_connection->port &&
105 mhttp_last_connection->is_ssl == mhttp_connection->is_ssl){
106 mhttp_connection->fd = mhttp_last_connection->fd;
107 mhttp_connection->ctx = mhttp_last_connection->ctx;
108 mhttp_connection->ssl = mhttp_last_connection->ssl;
109 mhttp_connection->meth = mhttp_last_connection->meth;
110 mhttp_connection->server_cert = mhttp_last_connection->server_cert;
111 #else
112 if (mhttp_last_connection != NULL &&
113 strcmp(mhttp_connection->host, mhttp_last_connection->host) == 0 &&
114 mhttp_last_connection->port == mhttp_connection->port){
115 mhttp_connection->fd = mhttp_last_connection->fd;
116 #endif
117 mhttp_debug("using the cached connection");
118 } else {
119 if (mhttp_last_connection != NULL){
120 #ifdef GOTSSL
121 if (mhttp_last_connection->is_ssl)
122 SSL_shutdown (mhttp_last_connection->ssl); /* send SSL/TLS close_notify */
123 #endif
124 close(mhttp_last_connection->fd);
125 #ifdef GOTSSL
126 if (mhttp_last_connection->is_ssl){
127 mhttp_debug("shuting down the ssl engine");
128 SSL_free (mhttp_last_connection->ssl);
129 SSL_CTX_free (mhttp_last_connection->ctx);
130 }
131 #endif
132 mhttp_end_conn(mhttp_last_connection);
133 mhttp_last_connection = NULL;
134 mhttp_debug("Closed the last connection");
135 }
136 newcon_flag = true;
137 mhttp_connection->fd = -1;
138 mhttp_debug("didnt find connection - creating a new one: %s/%d - %d ", mhttp_connection->host, mhttp_connection->port, mhttp_connection->fd);
139 #ifdef GOTSSL
140 if (mhttp_connection->is_ssl){
141 mhttp_debug("creating an SSL connection");
142 //SSLeay_add_ssl_algorithms();
143 OpenSSL_add_ssl_algorithms();
144 //mhttp_connection->meth = SSLv2_client_method();
145 mhttp_connection->meth = SSLv3_client_method();
146 SSL_load_error_strings();
147 mhttp_connection->ctx = SSL_CTX_new (mhttp_connection->meth);
148 if (mhttp_connection->ctx == NULL){
149 mhttp_debug("SSL_CTX_new failed - abort everything");
150 return -11;
151 }
152 if( (mhttp_connection->fd = mhttp_connect_inet_addr(mhttp_connection->host, mhttp_connection->port)) < 0) {
153 mhttp_debug("could not create a new socket: %d", mhttp_connection->fd);
154 return mhttp_connection->fd;
155 }
156 // XXX set SSL here
157
158 /* ----------------------------------------------- */
159 /* Now we have TCP conncetion. Start SSL negotiation. */
160
161 mhttp_debug("SSL craeting the ctx");
162 mhttp_connection->ssl = SSL_new (mhttp_connection->ctx);
163 if (mhttp_connection->ssl == NULL){
164 mhttp_debug("SSL_new failed - abort everything");
165 return -12;
166 }
167 mhttp_debug("SSL set_verify");
168 SSL_CTX_set_default_verify_paths(mhttp_connection->ctx);
169 //SSL_CTX_load_verify_locations(mhttp_connection->ctx, "/etc/httpd/conf/ssl.crt/ca-bundle.crt",
170 // NULL);
171 SSL_CTX_set_verify(mhttp_connection->ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, mhttp_verify_callback);
172 //SSL_CTX_set_verify(mhttp_connection->ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, NULL);
173 mhttp_debug("SSL set_fd");
174 SSL_set_fd (mhttp_connection->ssl, mhttp_connection->fd);
175 returnval = SSL_connect (mhttp_connection->ssl);
176 if (returnval == -1){
177 mhttp_debug("SSL_connect failed - abort everything");
178 ERR_print_errors_fp(stderr);
179 return -13;
180 }
181
182 /* Get the cipher - opt */
183 mhttp_debug ("SSL connection using %s\n", SSL_get_cipher (mhttp_connection->ssl));
184
185 /* Get server's certificate (note: beware of dynamic allocation) - opt */
186 mhttp_connection->server_cert = SSL_get_peer_certificate (mhttp_connection->ssl);
187 if (mhttp_connection->server_cert == NULL){
188 mhttp_debug("SSL_get_peer_certificate failed - abort everything");
189 return -14;
190 }
191
192 cert_str = X509_NAME_oneline (X509_get_subject_name (mhttp_connection->server_cert),0,0);
193 if (cert_str == NULL){
194 mhttp_debug("X509_get_subject_name failed - abort everything");
195 return -15;
196 }
197 mhttp_debug ("Certificate subject: %s\n", cert_str);
198 free (cert_str);
199
200 cert_str = X509_NAME_oneline (X509_get_issuer_name (mhttp_connection->server_cert),0,0);
201 if (cert_str == NULL){
202 mhttp_debug("X509_get_issuer_name failed - abort everything");
203 return -16;
204 }
205 mhttp_debug ("Certificate issuer: %s\n", cert_str);
206 free (cert_str);
207
208 /* We could do all sorts of certificate verification stuff here before
209 deallocating the certificate. */
210
211 if (SSL_get_verify_result(mhttp_connection->ssl) == X509_V_OK){
212 /* The client sent a certificate which verified OK */
213 mhttp_debug("certificate OK");
214 } else {
215 mhttp_debug("Certificate error: %s", X509_verify_cert_error_string(SSL_get_verify_result(mhttp_connection->ssl)));
216 }
217
218 X509_free (mhttp_connection->server_cert);
219
220 } else {
221 if( (mhttp_connection->fd = mhttp_connect_inet_addr(mhttp_connection->host, mhttp_connection->port)) < 0) {
222 mhttp_debug("could not create a new socket: %d", mhttp_connection->fd);
223 return mhttp_connection->fd;
224 }
225 }
226 #else
227 if( (mhttp_connection->fd = mhttp_connect_inet_addr(mhttp_connection->host, mhttp_connection->port)) < 0) {
228 mhttp_debug("could not create a new socket: %d", mhttp_connection->fd);
229 return mhttp_connection->fd;
230 }
231 #endif
232 }
233
234 mhttp_debug("socket descriptor is: %d ", mhttp_connection->fd);
235
236 len = 0;
237
238 // construct the query string
239 memset(str, 0, sizeof(str));
240
241 if ((req = construct_request(action, surl)) == NULL)
242 return -2;
243
244 returnval = write_socket(mhttp_connection, req, strlen(req));
245 if(returnval < 0)
246 {
247 /* write returns -1 on error */
248 perror("write(query string) error");
249 return -5;
250 }
251
252 if(returnval < strlen(req))
253 {
254 /* I'm not dealing with this error, regular programs should. */
255 perror("the query string write was short\n");
256 return -6;
257 }
258 free(req);
259 free(surl);
260 free(url);
261 free(host);
262
263 // if this is a PUT or POST - write the body
264 if (mhttp_body_set_flag)
265 {
266 // XXX we probably have a problem where 1024 is exceeded and need to rewrite
267 mhttp_debug("this is a %s.... writing the body...", action);
268 mhttp_debug("writing data: %s", mhttp_body);
269 returnval = write_socket(mhttp_connection, mhttp_body, strlen(mhttp_body));
270 if(returnval < strlen(mhttp_body))
271 {
272 /* I'm not dealing with this error, regular programs should. */
273 fprintf(stderr, "the write of %s data was short\n", action);
274 return -6;
275 }
276 returnval = write_socket(mhttp_connection, "\r\n", 2);
277 if(returnval < 2)
278 {
279 /* I'm not dealing with this error, regular programs should. */
280 fprintf(stderr, "the write of %s data - last line failed\n", action);
281 return -7;
282 }
283 }
284 free(action);
285
286
287 /* read off the response and split out headers and content */
288 mhttp_debug("starting output:");
289 found_hdrs = false;
290 rcode_flag = false;
291 len = 0;
292 curr_len = 0;
293 chunked = false;
294
295 if ((len = read_headers(mhttp_connection, str)) < 0){
296 // we have no headers
297 mhttp_debug("we have no headers ");
298 exit(1);
299 }
300 if (mhttp_response_length > 0){
301 buffer_size = mhttp_response_length;
302 } else {
303 buffer_size = MAX_STR;
304 }
305
306
307 mhttp_debug("initial len is: %d", len);
308 while((returnval = read_socket(mhttp_connection, str)) > 0)
309 {
310 *(str+returnval) = '\0';
311 // we have the headers - this is the body
312 if (mhttp_response_length > 0){
313 // we dont know how big it should be so compensate
314 if (mhttp_connection->is_chunked){
315 if (len + returnval >= buffer_size - 2){
316 memcpy(mhttp_response+len, str, (buffer_size - 2) - len);
317 pos = (buffer_size - 2) - len;
318 len += pos;
319 mhttp_debug("len at end of chunk is: %d", len);
320
321 // find the next chunk
322 mhttp_debug("looking for the next chunk");
323 ptr = str+pos;
324 rem = returnval - pos;
325
326 if ((this_chunk = find_chunk(mhttp_connection, &ptr, &rem)) > 0){
327 // resize and paste on remainder
328 mhttp_debug("resize and paste on remainder for next chunk processing");
329 mhttp_response = realloc(mhttp_response, buffer_size + this_chunk);
330 buffer_size += this_chunk;
331 memcpy(mhttp_response+len, ptr, rem);
332 returnval = rem;
333 } else if (this_chunk == 0){
334 // no more to come
335 mhttp_debug("we found the final chunk");
336 break;
337 }
338 } else {
339 // not end of chunk yet
340 memcpy(mhttp_response+len, str, returnval);
341 }
342 } else {
343 // make sure that it does not overflow the buffer
344 if (mhttp_response_length >= (len + returnval)){
345 memcpy(mhttp_response+len, str, returnval);
346 }
347 }
348 } else {
349 // we dont know how big it should be so compensate
350 // else this is not a chunked read - so realloc when necessary
351 if (len + returnval > buffer_size){
352 mhttp_response = realloc(mhttp_response, buffer_size + MAX_STR);
353 buffer_size += MAX_STR;
354 }
355 memcpy(mhttp_response+len, str, returnval);
356 }
357 len += returnval;
358
359
360 // lets get out of here if we have read enough
361 if (mhttp_response_length > 0 && len >= mhttp_response_length )
362 break;
363 }
364 mhttp_debug("content length actually copied: %d (may include \\r\\n)", len);
365 mhttp_response_length = len;
366
367 /* it will be closed anyway when we exit */
368 if (mhttp_protocol == 0 ||
369 (clptr = strstr(mhttp_resp_headers, "Connection: close")) ||
370 (clptr = strstr(mhttp_resp_headers, "Connection: Close")) ){
371 #ifdef GOTSSL
372 if (mhttp_connection->is_ssl)
373 SSL_shutdown (mhttp_connection->ssl); /* send SSL/TLS close_notify */
374 #endif
375 /* Clean up. */
376 close(mhttp_connection->fd);
377 #ifdef GOTSSL
378 if (mhttp_connection->is_ssl){
379 mhttp_debug("shuting down the ssl engine");
380 SSL_free (mhttp_connection->ssl);
381 SSL_CTX_free (mhttp_connection->ctx);
382 }
383 #endif
384 if (mhttp_last_connection != NULL){
385 mhttp_end_conn(mhttp_last_connection);
386 mhttp_last_connection = NULL;
387 mhttp_debug("removed socket name");
388 }
389 mhttp_debug("Closed the connection");
390 } else {
391 if (newcon_flag){
392 if (mhttp_last_connection != NULL){
393 mhttp_end_conn(mhttp_last_connection);
394 mhttp_last_connection = NULL;
395 }
396 mhttp_last_connection = mhttp_new_conn();
397 mhttp_last_connection->host = strdup(mhttp_connection->host);
398 mhttp_last_connection->port = mhttp_connection->port;
399 mhttp_last_connection->fd = mhttp_connection->fd;
400 #ifdef GOTSSL
401 if (mhttp_connection->is_ssl){
402 mhttp_debug("saving SSL stuff");
403 mhttp_last_connection->is_ssl = mhttp_connection->is_ssl;
404 mhttp_last_connection->ssl = mhttp_connection->ssl;
405 mhttp_last_connection->ctx = mhttp_connection->ctx;
406 }
407 #endif
408 mhttp_debug("Caching the connection");
409 } else {
410 mhttp_debug("connection allready cached");
411 }
412 }
413 mhttp_end_conn(mhttp_connection);
414 mhttp_connection = NULL;
415 mhttp_debug("all done");
416 return 1;
417 }
418
419
420 int find_content_length(void){
421 char *ptr;
422 int rem = 0;
423
424 // determine the Content-Length header
425 if ((ptr = strstr(mhttp_resp_headers, "Content-Length:")) ||
426 (ptr = strstr(mhttp_resp_headers, "Content-length:"))){
427 mhttp_debug("found content-length");
428 ptr += 16;
429 mhttp_response_length = atoi(ptr);
430 mhttp_debug("content length: %d", mhttp_response_length);
431 mhttp_response = malloc(mhttp_response_length + 2);
432 return mhttp_response_length;
433 }
434 return 0;
435
436 }
437
438
439 bool find_transfer_encoding(void){
440 char *clptr;
441 int this_chunk;
442
443 /* look for Transfer-Encoding: chunked */
444 // clptr the chunk pointer
445 // ptr the beginning of the body
446 if ((clptr = strstr(mhttp_resp_headers, "Transfer-Encoding:")) ||
447 (clptr = strstr(mhttp_resp_headers, "Transfer-encoding:"))){
448 clptr += 19;
449 if (strncmp(clptr, "chunked",7) == 0){
450 mhttp_debug("found Transfer-Encoding: chunked");
451 return true;
452 }
453 }
454 return false;
455
456 }
457
458
459 int find_chunk(mhttp_conn_t conn, char **ptr, int *rem){
460 char *clptr;
461 char *myptr;
462 int myrem;
463 int diff;
464 int this_chunk;
465 int returnval;
466
467 /* look for Transfer-Encoding: chunked */
468 // clptr the chunk pointer
469 // ptr the beginning of the body
470
471 myptr = *ptr;
472 myrem = *rem;
473
474 // make sure that there is enuf of ptr left
475 mhttp_debug("remainder is: %d", myrem);
476 if (myrem <= 2 || !(clptr = strstr(myptr, "\r\n"))){
477 mhttp_debug("getting another line");
478 if ((returnval = read_socket(conn, myptr+myrem)) > 0){
479 myrem += returnval;
480 *(myptr+myrem) = '\0';
481 mhttp_debug("got another line: %d - #%s#", returnval, myptr);
482 } else {
483 mhttp_debug("cant get another line - aborting");
484 return -17;
485 }
486 }
487
488 // must find the end of the chunked length line
489 // in the remainding buffer
490 if (clptr = strstr(myptr, "\r\n")){
491 mhttp_debug("looking for chunk in: %s#", myptr);
492 if (sscanf(myptr, "%x\r\n", &this_chunk) != 1){
493 mhttp_debug("count not the chunked amount - something ify");
494 if ((returnval = read_socket(conn, myptr+myrem)) > 0){
495 myrem += returnval;
496 *(myptr+myrem) = '\0';
497 mhttp_debug("got another line: %d - #%s#", returnval, myptr);
498 if (strncmp(myptr, "\r\n", 2) == 0){
499 myptr+=2;
500 myrem-=2;
501 }
502 mhttp_debug("looking for chunk in: #%s#", myptr);
503 if (sscanf(myptr, "%x\r\n", &this_chunk) != 1){
504 mhttp_debug("count not the chunked amount - something broken");
505 return -17;
506 }
507 }
508 return -17;
509 }
510 // shift past the chunk size
511 *clptr = '\0';
512 myrem -= strlen(myptr) + 2;
513 clptr += 2;
514 mhttp_debug("Transfer-Encoding: chunked buffer is %d - %d bytes left: %s", this_chunk, myrem, clptr+myrem);
515
516 *ptr = clptr;
517 *rem = myrem;
518 return this_chunk;
519 }
520 return -17;
521
522 }
523
524
525
526 int read_headers(mhttp_conn_t conn, char *str){
527 int returnval;
528 int curr_len;
529 int rem;
530 int this_chunk;
531 char *ptr, *eomsg;
532 bool rcode_flag;
533
534
535 rcode_flag = false;
536 curr_len = 0;
537 while((returnval = read_socket(conn, str)) > 0)
538 {
539 *(str+returnval) = '\0';
540 mhttp_debug("Header line %d: %s", returnval, str);
541
542 if (strlen(mhttp_resp_headers) + returnval > MAX_HDR_STR){
543 mhttp_debug("have not found the headers within MAX_HDR_STR: %d", MAX_HDR_STR);
544 return -18;
545 }
546
547 /* detect the end of the headers */
548 sprintf(mhttp_resp_headers+strlen(mhttp_resp_headers), "%s", str);
549
550 /* find the return code */
551 if (!rcode_flag &&
552 strncmp(str, "HTTP/",5) == 0 &&
553 (strncmp(str+5, "0.9 ",4) == 0 ||
554 strncmp(str+5, "1.0 ",4) == 0 ||
555 strncmp(str+5, "1.1 ",4) == 0 ) ){
556 ptr = str+9;
557 *(ptr+3) = '\0';
558 mhttp_rcode = atoi(ptr);
559 rcode_flag = true;
560 ptr+=4;
561 /* find the status reason */
562 if ((eomsg = strstr(ptr, "\r\n")) || (eomsg = strstr(ptr, "\n"))){
563 *eomsg = '\0';
564 mhttp_reason = strdup(ptr);
565 }
566 mhttp_debug("detected return code: %d - %s", mhttp_rcode, mhttp_reason);
567 }
568
569 if ((ptr = strstr(mhttp_resp_headers, "\r\n\r\n")) ||
570 (ptr = strstr(mhttp_resp_headers, "\n\n"))){
571 *ptr = '\0';
572 mhttp_debug("found end of headers at: %d", strlen(mhttp_resp_headers));
573 mhttp_debug("headers are: %s", mhttp_resp_headers);
574 if (strncmp(ptr,"\0\n\r\n",4) == 0){
575 /* how far along the current buffer is the eoh marker */
576 curr_len = (strlen(mhttp_resp_headers) + 4) - curr_len;
577 ptr+=4;
578 } else {
579 curr_len = (strlen(mhttp_resp_headers) + 2) - curr_len;
580 ptr+=2;
581 }
582
583 /* tidy up the first bit of the body XXX */
584 mhttp_debug("returnval: %d - curr_len: %d", returnval, curr_len);
585 rem = returnval - curr_len;
586 mhttp_debug("the remainder is: %d", rem);
587
588 // find the Content-Length header
589 if ( find_content_length() > 0 ){
590 if (mhttp_response_length >= rem){
591 mhttp_debug("copying the initial part of the body: %s", ptr);
592 memcpy(mhttp_response, ptr, rem);
593 return rem;
594 } else {
595 // serious error - cant determine length properly
596 mhttp_debug("serious error - cant determine length properly");
597 return -8;
598 }
599 // or find the Tranfer-Enconding: chunked header
600 } else if (find_transfer_encoding()){
601 // find the chunk value
602 conn->is_chunked = true;
603 if ((this_chunk = find_chunk(conn, &ptr, &rem)) > 0){
604 mhttp_response = malloc(this_chunk + 2);
605 memcpy(mhttp_response, ptr, rem);
606 mhttp_response_length = this_chunk + 2;
607 return rem;
608 } else if (this_chunk == 0){
609 // an empty body
610 return 0;
611 } else {
612 // failed to find the next chunk value
613 mhttp_debug("cannot find \\r\\n after first chunked marker - time to give up");
614 return -17;
615 }
616 } else {
617 mhttp_debug("didnt find content-length - must use realloc: %d", rem);
618 mhttp_response_length = 0;
619 mhttp_response = malloc(MAX_STR);
620 memcpy(mhttp_response, ptr, rem);
621 return rem;
622 }
623 // or determine that it is HTTP/1.0
624 // or find the Connection: close
625 return curr_len;
626 }
627 curr_len += returnval;
628 }
629
630 /* must have hit an error */
631 return returnval;
632
633 }
634
635
636 int check_url(char *purl, char **url, char **host){
637
638 char *ptrhost;
639
640 if(strlen(purl) == 0)
641 {
642 mhttp_debug("must supply a url");
643 return -3;
644 }
645
646 if (strncmp(purl, "http://", 7) == 0){
647 ptrhost = purl+7;
648 #ifdef GOTSSL
649 } else if (strncmp(purl, "https://", 8) == 0){
650 ptrhost = purl+8;
651 mhttp_debug("setting the ssl flag");
652 mhttp_connection->is_ssl = true;
653 #endif
654 } else {
655 mhttp_debug("url must start with http:// - and yep we dont support https\n");
656 return -4;
657 }
658 *url = strdup(purl);
659 *host = strdup(ptrhost);
660 mhttp_debug("begin of host is: %s", *host);
661 return 0;
662 }
663
664
665
666 int get_port_and_uri(char *url, char *host, char **surl){
667 char *ptr;
668 int port;
669
670 // hunt for the beginning of the uri
671 mhttp_debug("begin looking for host at: %s", host);
672 *surl = malloc(MAX_STR);
673 ptr = strstr(host, "/");
674 if (ptr != NULL){
675 *ptr = '\0';
676 ptr++;
677 sprintf(*surl, "/%s", ptr);
678 } else {
679 sprintf(*surl, "/");
680 }
681 // hunt for the beginning of the port
682 ptr = strstr(host, ":");
683 if (ptr != NULL){
684 *ptr = '\0';
685 ptr++;
686 port = atoi(ptr);
687 } else {
688 #ifdef GOTSSL
689 if (strncmp(url ,"https", 5) == 0){
690 port = 443;
691 } else {
692 port = 80;
693 }
694 #else
695 port = 80;
696 #endif
697
698 }
699 return port;
700
701 }
702
703
704 #ifdef GOTSSL
705 static int mhttp_verify_callback(int ok, X509_STORE_CTX* ctx)
706 {
707 return 1;
708 }
709 #endif
710
711 mhttp_conn_t mhttp_new_conn(void){
712 mhttp_conn_t new_conn;
713
714 new_conn = (mhttp_conn_t) malloc(sizeof(struct mhttp_conn_st));
715 memset(new_conn, 0, sizeof(struct mhttp_conn_st));
716
717 new_conn->host = NULL;
718 new_conn->port = 0;
719 new_conn->is_ssl = false;
720 new_conn->is_chunked = false;
721 return new_conn;
722 }
723
724
725 void mhttp_end_conn(mhttp_conn_t conn){
726
727 mhttp_debug("resetting conn");
728 free(conn->host);
729 free(conn);
730
731 }
732
733
734 char *construct_request(char *action, char *url){
735 char *str;
736 int i;
737
738 str = malloc(MAX_HDR_STR);
739
740 strcpy(str, action);
741 strcpy(str+strlen(str), " ");
742 strcpy(str+strlen(str), url);
743 sprintf(str+strlen(str), " HTTP/1.%d\r\n", mhttp_protocol);
744 mhttp_debug("adding on the headers: %d", mhttp_hcnt);
745 for(i = 0; i < mhttp_hcnt; i++)
746 {
747 // make sure that we dont exceed the buffer
748 if ((strlen(str) + strlen(mhttp_headers[i])) > MAX_BUFFERS - 1)
749 break;
750 mhttp_debug("adding header: %s", mhttp_headers[i]);
751 sprintf(str+strlen(str), "%s\r\n", mhttp_headers[i]);
752 }
753 // if this is a post - add the Content-Length header
754 if (mhttp_body_set_flag)
755 {
756 sprintf(str+strlen(str),
757 "Content-Length: %d\r\n\r\n", strlen(mhttp_body));
758 } else {
759 strcpy(str+strlen(str), "\r\n\r\n");
760 }
761 mhttp_debug("query string + headers are: %s", str);
762
763 return str;
764
765 }
766
767
768 int check_action(char *paction, char **action){
769 if(strlen(paction) == 0)
770 {
771 mhttp_debug("must supply an action");
772 return -2;
773 }
774
775 if (strcmp(paction, "GET") != 0 &&
776 strcmp(paction, "POST") != 0 &&
777 strcmp(paction, "PUT") != 0 &&
778 strcmp(paction, "DELETE") != 0 &&
779 strcmp(paction, "HEAD") != 0)
780 {
781 mhttp_debug("must supply an action of GET, PUT, POST, DELETE, or HEAD");
782 return -1;
783 }
784 *action = strdup(paction);
785 mhttp_debug("The action is: %s", *action);
786 return 0;
787 }
788
789
790 void mhttp_switch_debug(int set)
791 {
792 if (!mhttp_first_init)
793 mhttp_init();
794
795 if (set > 0)
796 {
797 mhttp_lets_debug = true;
798 #ifdef GOTSSL
799 mhttp_debug("%s", "switched on debugging(SSL Support running)...");
800 #else
801 mhttp_debug("%s", "switched on debugging...");
802 #endif
803 } else {
804 mhttp_lets_debug = false;
805 }
806
807 }
808
809
810 void mhttp_set_protocol(int proto)
811 {
812
813 if (!mhttp_first_init)
814 mhttp_init();
815 mhttp_protocol = proto;
816
817 }
818
819
820 int mhttp_get_status_code(void)
821 {
822
823 return mhttp_rcode;
824
825 }
826
827
828 int mhttp_get_response_length(void)
829 {
830
831 return mhttp_response_length;
832
833 }
834
835
836 char *mhttp_get_reason(void)
837 {
838
839 if (mhttp_reason != NULL){
840 mhttp_debug("the reason is: %s", mhttp_reason);
841 return strdup(mhttp_reason);
842 } else {
843 return NULL;
844 }
845
846 }
847
848
849 char *mhttp_get_response(void)
850 {
851
852 return mhttp_response;
853
854 }
855
856
857 char *mhttp_get_response_headers(void)
858 {
859
860 return strdup(mhttp_resp_headers);
861
862 }
863
864
865 void mhttp_reset(void)
866 {
867
868 int i;
869
870 if (!mhttp_first_init)
871 mhttp_init();
872 if (mhttp_response != NULL){
873 free(mhttp_response);
874 mhttp_response = NULL;
875 mhttp_debug("reset the response");
876 }
877 mhttp_response_length = 0;
878 if (mhttp_reason != NULL){
879 free(mhttp_reason);
880 mhttp_reason = NULL;
881 mhttp_debug("reset the reason");
882 }
883 if (mhttp_body_set_flag)
884 free(mhttp_body);
885 mhttp_body_set_flag = false;
886 mhttp_rcode = 0;
887
888 mhttp_debug("finished reset");
889
890 }
891
892
893 void mhttp_init(void)
894 {
895
896 int i;
897
898 mhttp_first_init = true;
899 for (i = 0; i < mhttp_hcnt; i++)
900 {
901 free(mhttp_headers[i]);
902 mhttp_debug("freeing header");
903 mhttp_headers[i] = NULL;
904 }
905 mhttp_hcnt = 0;
906 mhttp_lets_debug = false;
907 mhttp_protocol = 0;
908 mhttp_host_hdr = false;
909 mhttp_reset();
910 mhttp_debug("finished init");
911 }
912
913
914 void mhttp_add_header(char *hdr)
915 {
916
917 if (!mhttp_first_init)
918 mhttp_init();
919
920 /* Do we have a Host Header? */
921 if (! mhttp_host_hdr && strncmp("Host:", hdr, 5) == 0)
922 mhttp_host_hdr = true;
923
924 mhttp_headers[mhttp_hcnt++] = strdup(hdr);
925 mhttp_debug("request header %s", mhttp_headers[mhttp_hcnt - 1]);
926 mhttp_headers[mhttp_hcnt] = NULL;
927
928 }
929
930
931 void mhttp_set_body(char *bdy)
932 {
933
934 // assumes body is a string XXX
935 if (!mhttp_first_init)
936 mhttp_init();
937 mhttp_body = strdup(bdy);
938 mhttp_debug("setting body: %s", mhttp_body);
939 mhttp_body_set_flag = true;
940
941 }
942
943
944 int read_socket(mhttp_conn_t conn, void *buf){
945 int returnval;
946
947 #ifdef GOTSSL
948 if (conn->is_ssl){
949 returnval = SSL_read (conn->ssl, buf, READ_BUF);
950 if (returnval == -1){
951 mhttp_debug("SSL_read failed - abort everything");
952 ERR_print_errors_fp(stderr);
953 return -16;
954 }
955 } else {
956 returnval = read(conn->fd, buf, READ_BUF);
957 }
958 #else
959 returnval = read(conn->fd, buf, READ_BUF);
960 #endif
961 return returnval;
962
963 }
964
965
966
967 int write_socket(mhttp_conn_t conn, const void *buf, size_t count){
968 int returnval;
969
970 #ifdef GOTSSL
971 if (conn->is_ssl){
972 //mhttp_debug("writing to ssl connection");
973 returnval = SSL_write (conn->ssl, buf, count);
974 if (returnval == -1){
975 mhttp_debug("SSL_write failed - abort everything");
976 ERR_print_errors_fp(stderr);
977 return -17;
978 }
979 } else {
980 returnval = write(conn->fd, buf, count);
981 }
982 #else
983 returnval = write(conn->fd, buf, count);
984 #endif
985 return returnval;
986
987 }
988
989
990 int mhttp_connect_inet_addr(const char *hostname, unsigned short int port)
991 {
992 int inet_socket; /* socket descriptor */
993 struct sockaddr_in inet_address; /* IP/port of the remote host to connect to */
994
995 if ( mhttp_build_inet_addr(&inet_address, hostname, port) < 0 )
996 return -1;
997
998 /* socket(domain, type, protocol) */
999 inet_socket = socket(PF_INET, SOCK_STREAM, 0);
1000
1001 mhttp_debug("socket no: %d", inet_socket);
1002 /* domain is PF_INET(internet/IPv4 domain) *
1003 * type is SOCK_STREAM(tcp) *
1004 * protocol is 0(only one SOCK_STREAM type in the PF_INET domain
1005 */
1006
1007 if (inet_socket < 0)
1008 {
1009 /* socket returns -1 on error */
1010 perror("socket(PF_INET, SOCK_STREAM, 0) error");
1011 mhttp_debug("socket(PF_INET, SOCK_STREAM, 0) error");
1012 return -2;
1013 }
1014
1015 /* connect(sockfd, serv_addr, addrlen) */
1016 if(connect(inet_socket, (struct sockaddr *)&inet_address, sizeof(struct sockaddr_in)) < 0)
1017 {
1018 /* connect returns -1 on error */
1019 perror("connect(...) error");
1020 mhttp_debug("connect(...) error");
1021 return -3;
1022 }
1023
1024 return inet_socket;
1025 }
1026
1027
1028 int mhttp_build_inet_addr(struct sockaddr_in *addr, const char *hostname, unsigned short int port)
1029 {
1030 struct hostent *host_entry;
1031
1032 /* gethostbyname(name) */
1033 host_entry = gethostbyname(hostname);
1034
1035 if(host_entry == NULL)
1036 {
1037 /* gethostbyname returns NULL on error */
1038 herror("gethostbyname failed");
1039 mhttp_debug("gethostbyname failed");
1040 return -1;
1041 }
1042
1043 /* memcpy(dest, src, length) */
1044 memcpy(&addr->sin_addr.s_addr, host_entry->h_addr_list[0], host_entry->h_length);
1045 /* copy the address to the sockaddr_in struct. */
1046
1047 /* set the family type (PF_INET) */
1048 addr->sin_family = host_entry->h_addrtype;
1049
1050 /* addr->sin_port = port won't work because they are different byte
1051 * orders
1052 */
1053 addr->sin_port = htons(port);
1054
1055 /* just to be pedantic... */
1056 return 1;
1057 }
1058
1059 /* debug logging */
1060 void mhttp_debug(const char *msgfmt, ...)
1061 {
1062 va_list ap;
1063 char *pos, message[MAX_STR];
1064 int sz;
1065 time_t t;
1066
1067 if (!mhttp_lets_debug)
1068 return;
1069
1070 /* timestamp */
1071 t = time(NULL);
1072 pos = ctime(&t);
1073 sz = strlen(pos);
1074 /* chop off the \n */
1075 pos[sz-1]='\0';
1076
1077 /* insert the header */
1078
1079 snprintf(message, MAX_STR, "mhttp debug:%s: ", pos);
1080
1081 /* find the end and attach the rest of the msg */
1082 for (pos = message; *pos != '\0'; pos++); //empty statement
1083 sz = pos - message;
1084 va_start(ap, msgfmt);
1085 vsnprintf(pos, MAX_STR - sz, msgfmt, ap);
1086 fprintf(stderr,"%s", message);
1087 fprintf(stderr, "\n");
1088 fflush(stderr);
1089 }
1090
1091