原创 https/http兼容客户端的c语言实现

2011-6-14 20:43 3627 5 8 分类: MCU/ 嵌入式
 
在最近的工作中碰到了需要跟服务器创建https连接并发送请求的问题,但之前已经写好的代码都是针对传统的tcp套接字来实现的。所以到处找一种比较简洁的方法可以不对原有的代码做太大的改动而能根据需要处理https的需求。把一点结果分享给大家。本文提供的源代码只适用于Unix/Linux系统,Windows下的只要对套接字部分进行修改即可。所有实现基于OpenSSL接口。

本文对于普通的套接字采用了non-blocking方式来进行连接,但似乎通过这样得到的套接字来创建一个ssl连接会得到连接失败的错误。目前偶还搞不清楚为什么,所以只好针对不同的传入参数来决定是否采用block方式创建socket连接。哪位大虾如果知道为什么,望不吝赐教。

另外如果你要进行纯粹的安全连接的话,可以使用BIO方式直接实现,而不需先创建传统的socket套接字再进行ssl层的处理。

关于BIO方式的实现,敝人不在此篇文章详细描述。有兴趣的网友可以去看一篇写的很不错的文章:使用 OpenSSL API 进行安全编程

源代码如下: language=javascript src="/plug-ins/SyntaxHighlighter/shCore.js">

  1. /*  
  2.  * OpenSSL SSL/TLS Https Client example  
  3.  * Only for Unix/Linux:  
  4.  *    cc -c https.c  
  5.  *    cc -o https https.c -lssl  
  6.  * OpenSSL library needed.  
  7.  *  
  8.  * 同时支持普通的socket连接以及基于普通socket基础之上的ssl  
  9.  * 连接。这对于已有的socket程序修改来说会比较方便,不至于  
  10.  * 和原来的结构发生太大的冲突.  
  11.  * 要注意的一点,似乎当使用socket套接字来创建ssl连接的时候,  
  12.  * 如果套接字是采用非阻塞方式建立的话,会导致ssl会话失败,不  
  13.  * 知道为什么。所以这里对于提供给https的套接字采用了普通的  
  14.  * connect方法创建。  
  15.  *  
  16.  */  
  17.   
  18.   
  19. #include <stdio.h>   
  20.   
  21. #include <stdlib.h>   
  22.   
  23. #include <string.h>   
  24.   
  25. #include <stdarg.h>   
  26.   
  27. #include <errno.h>   
  28.   
  29. #include <fcntl.h>   
  30.   
  31. #include <unistd.h>   
  32.   
  33. #include <sys/types.h>   
  34.   
  35. #include <sys/socket.h>   
  36.   
  37. #include <netinet/in.h>   
  38.   
  39. #include <arpa/inet.h>   
  40.   
  41. #include <netdb.h>   
  42.   
  43.   
  44. #include <openssl/crypto.h>   
  45.   
  46. #include <openssl/ssl.h>   
  47.   
  48. #include <openssl/err.h>   
  49.   
  50. #include <openssl/rand.h>   
  51.   
  52.   
  53. #define BUF_LEN 1024   
  54.   
  55. #define MAX_STRING_LEN  2048   
  56.   
  57.   
  58. //xnet_select x defines   
  59.   
  60. #define READ_STATUS     0   
  61.   
  62. #define WRITE_STATUS    1   
  63.   
  64. #define EXCPT_STATUS    2   
  65.   
  66.   
  67. /* flag to set request with ssl or not. */  
  68.   
  69. static int bIsHttps = 1;   
  70.   
  71.   
  72. static int timeout_sec = 10;   
  73.   
  74. static int timeout_microsec = 0;   
  75.   
  76.   
  77. void err_doit(int errnoflag, const char *fmt, va_list ap);   
  78.   
  79. void err_quit(const char *fmt, ...);   
  80.   
  81. int create_tcpsocket(const char *host, const unsigned short port);   
  82.   
  83. int xnet_select(int s, int sec, int usec, short x);   
  84.   
  85.   
  86. int main(int argc, char* argv[]){   
  87.   
  88.     char* host = "127.0.0.1";   
  89.   
  90.     unsigned short port = 80;   
  91.   
  92.   
  93.     int fd;   
  94.   
  95.        
  96.   
  97.     SSL *ssl;   
  98.   
  99.     SSL_CTX *ctx;   
  100.   
  101.   
  102.     int n,ret;   
  103.   
  104.   
  105.     char buf[BUF_LEN];   
  106.   
  107.     char* requestpath = "/";   
  108.   
  109.   
  110.     if( argc == 5 ){   
  111.   
  112.         host = argv[1];   
  113.   
  114.         port = atoi(argv[2]);   
  115.   
  116.         requestpath = argv[3];   
  117.   
  118.         bIsHttps = atoi(argv[4]);   
  119.   
  120.     }   
  121.   
  122.   
  123.     /* make connection to the cache server */  
  124.   
  125.     fd = create_tcpsocket(host, port);   
  126.   
  127.   
  128.     /* http request. */  
  129.   
  130.     sprintf(buf, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n",requestpath, host);   
  131.   
  132.   
  133.     if(bIsHttps != 1){   
  134.   
  135.         if(xnet_select(fd, timeout_sec, timeout_microsec, WRITE_STATUS)>0){   
  136.   
  137.             /* send off the message */  
  138.   
  139.             write(fd, buf, strlen(buf));   
  140.   
  141.         }   
  142.   
  143.         else{   
  144.   
  145.             err_quit("Socket I/O Write Timeout %s:%d\n", host, port);   
  146.   
  147.         }   
  148.   
  149.         printf("Server response:\n");   
  150.   
  151.         while (xnet_select(fd, timeout_sec, timeout_microsec, READ_STATUS)>0){   
  152.   
  153.             if ((n = read(fd, buf, BUF_LEN-1)) > 0) {   
  154.   
  155.                 buf[n] = '\0';   
  156.   
  157.                 printf("%s", buf);   
  158.   
  159.             }   
  160.   
  161.             else{   
  162.   
  163.                 break;   
  164.   
  165.             }   
  166.   
  167.         }   
  168.   
  169.         // close the plain socket handler.   
  170.   
  171.         close(fd);   
  172.   
  173.     }   
  174.   
  175.     else{   
  176.   
  177.         SSL_load_error_strings();   
  178.   
  179.         SSL_library_init();   
  180.   
  181.         ctx = SSL_CTX_new(SSLv23_client_method());   
  182.   
  183.         if ( ctx == NULL ){   
  184.   
  185.             err_quit("init SSL CTX failed:%s\n",   
  186.   
  187.                         ERR_reason_error_string(ERR_get_error()));   
  188.   
  189.         }   
  190.   
  191.            
  192.   
  193.         ssl = SSL_new(ctx);   
  194.   
  195.         if ( ssl == NULL ){   
  196.   
  197.             err_quit("new SSL with created CTX failed:%s\n",   
  198.   
  199.                         ERR_reason_error_string(ERR_get_error()));   
  200.   
  201.         }   
  202.   
  203.   
  204.         ret = SSL_set_fd(ssl, fd);   
  205.   
  206.         if ( ret == 0 ){   
  207.   
  208.             err_quit("add SSL to tcp socket failed:%s\n",   
  209.   
  210.                         ERR_reason_error_string(ERR_get_error()));   
  211.   
  212.         }   
  213.   
  214.   
  215.         /* PRNG */  
  216.   
  217.         RAND_poll();   
  218.   
  219.         while ( RAND_status() == 0 ){   
  220.   
  221.             unsigned short rand_ret = rand() % 65536;   
  222.   
  223.             RAND_seed(&rand_ret, sizeof(rand_ret));   
  224.   
  225.         }   
  226.   
  227.   
  228.         /* SSL Connect */  
  229.   
  230.         ret = SSL_connect(ssl);   
  231.   
  232.         if( ret != 1 ){   
  233.   
  234.             err_quit("SSL connection failed:%s\n",   
  235.   
  236.                         ERR_reason_error_string(ERR_get_error()));   
  237.   
  238.         }   
  239.   
  240.   
  241.         // https socket write.   
  242.   
  243.         SSL_write(ssl, buf, strlen(buf));   
  244.   
  245.         while((n = SSL_read(ssl, buf, BUF_LEN-1)) > 0){   
  246.   
  247.             buf[n] = '\0';   
  248.   
  249.             write(1, buf, n);               
  250.   
  251.         }   
  252.   
  253.         if(n != 0){   
  254.   
  255.             err_quit("SSL read failed:%s\n",   
  256.   
  257.                         ERR_reason_error_string(ERR_get_error()));   
  258.   
  259.         }   
  260.   
  261.         // close ssl tunnel.   
  262.   
  263.         ret = SSL_shutdown(ssl);    
  264.   
  265.         if( ret != 1 ){   
  266.   
  267.             close(fd);   
  268.   
  269.             err_quit("SSL shutdown failed:%s\n",   
  270.   
  271.                         ERR_reason_error_string(ERR_get_error()));   
  272.   
  273.         }   
  274.   
  275.            
  276.   
  277.         // close the plain socket handler.   
  278.   
  279.         close(fd);   
  280.   
  281.   
  282.         // clear ssl resource.   
  283.   
  284.         SSL_free(ssl);    
  285.   
  286.         SSL_CTX_free(ctx);   
  287.   
  288.         ERR_free_strings();   
  289.   
  290.     }   
  291.   
  292. }   
  293.   
  294.   
  295. /* create common tcp socket connection */  
  296.   
  297. int create_tcpsocket(const char *host, const unsigned short port){   
  298.   
  299.     int ret;   
  300.   
  301.   
  302.     char * transport = "tcp";   
  303.   
  304.     struct hostent *phe; /* pointer to host information entry */  
  305.   
  306.     struct protoent *ppe; /* pointer to protocol information entry */  
  307.   
  308.     struct sockaddr_in sin; /* an Internet endpoint address */  
  309.   
  310.     int s; /* socket descriptor and socket type */  
  311.   
  312.        
  313.   
  314.     memset(&sin, 0, sizeof(sin));   
  315.   
  316.     sin.sin_family = AF_INET;   
  317.   
  318.        
  319.   
  320.     if ((sin.sin_port = htons(port)) == 0)   
  321.   
  322.         err_quit("invalid port \"%d\"\n", port);   
  323.   
  324.        
  325.   
  326.     /* Map host name to IP address, allowing for dotted decimal */  
  327.   
  328.     if( phe = gethostbyname(host) )   
  329.   
  330.         memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);   
  331.   
  332.     else if( (sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE )   
  333.   
  334.         err_quit("can't get \"%s\" host entry\n", host);   
  335.   
  336.        
  337.   
  338.     /* Map transport protocol name to protocol number */  
  339.   
  340.     if ( (ppe = getprotobyname(transport)) == 0)   
  341.   
  342.         err_quit("can't get \"%s\" protocol entry\n", transport);   
  343.   
  344.        
  345.   
  346.     /* Allocate a common TCP socket */  
  347.   
  348.     s = socket(PF_INET, SOCK_STREAM, ppe->p_proto);   
  349.   
  350.     if (s < 0)   
  351.   
  352.         err_quit("can't create socket: %s\n", strerror(errno));   
  353.   
  354.        
  355.   
  356.     if(bIsHttps != 1){   
  357.   
  358.         /* Connect the socket with timeout */  
  359.   
  360.         fcntl(s,F_SETFL, O_NONBLOCK);   
  361.   
  362.         if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) == -1){   
  363.   
  364.             if (errno == EINPROGRESS){// it is in the connect process    
  365.   
  366.                 struct timeval tv;    
  367.   
  368.                 fd_set writefds;    
  369.   
  370.                 tv.tv_sec = timeout_sec;    
  371.   
  372.                 tv.tv_usec = timeout_microsec;    
  373.   
  374.                 FD_ZERO(&writefds);    
  375.   
  376.                 FD_SET(s, &writefds);    
  377.   
  378.                 if(select(s+1,NULL,&writefds,NULL,&tv)>0){    
  379.   
  380.                     int len=sizeof(int);    
  381.   
  382.                     //下面的一句一定要,主要针对防火墙    
  383.   
  384.                     getsockopt(s, SOL_SOCKET, SO_ERROR, &errno, &len);    
  385.   
  386.                     if(errno != 0)    
  387.   
  388.                         ret = 1;   
  389.   
  390.                     else  
  391.   
  392.                         ret = 0;   
  393.   
  394.                 }   
  395.   
  396.                 else  
  397.   
  398.                     ret = 2;//timeout or error happen    
  399.   
  400.             }   
  401.   
  402.             else ret = 1;    
  403.   
  404.   
  405.         }   
  406.   
  407.         else{   
  408.   
  409.             ret = 1;   
  410.   
  411.         }   
  412.   
  413.     }   
  414.   
  415.     else{   
  416.   
  417.         /* create common tcp socket.seems non-block type is not supported by ssl. */  
  418.   
  419.         ret = connect(s, (struct sockaddr *)&sin, sizeof(sin));   
  420.   
  421.     }   
  422.   
  423.   
  424.     if(ret != 0){   
  425.   
  426.         close(s);   
  427.   
  428.         err_quit("can't connect to %s:%d\n", host, port);   
  429.   
  430.     }   
  431.   
  432.   
  433.     return s;   
  434.   
  435. }   
  436.   
  437.   
  438. /*  
  439. s    - SOCKET  
  440. sec  - timeout seconds  
  441. usec - timeout microseconds  
  442. x    - select status  
  443. */  
  444.   
  445. int xnet_select(int s, int sec, int usec, short x){   
  446.   
  447.     int st = errno;   
  448.   
  449.     struct timeval to;   
  450.   
  451.     fd_set fs;   
  452.   
  453.     to.tv_sec = sec;   
  454.   
  455.     to.tv_usec = usec;   
  456.   
  457.     FD_ZERO(&fs);   
  458.   
  459.     FD_SET(s, &fs);   
  460.   
  461.     switch(x){   
  462.   
  463.         case READ_STATUS:   
  464.   
  465.         st = select(s+1, &fs, 0, 0, &to);   
  466.   
  467.         break;   
  468.   
  469.         case WRITE_STATUS:   
  470.   
  471.         st = select(s+1, 0, &fs, 0, &to);   
  472.   
  473.         break;   
  474.   
  475.         case EXCPT_STATUS:   
  476.   
  477.         st = select(s+1, 0, 0, &fs, &to);   
  478.   
  479.         break;   
  480.   
  481.     }   
  482.   
  483.     return(st);   
  484.   
  485. }   
  486.   
  487.   
  488. void err_doit(int errnoflag, const char *fmt, va_list ap){   
  489.   
  490.     int errno_save;   
  491.   
  492.     char buf[MAX_STRING_LEN];   
  493.   
  494.   
  495.     errno_save = errno;    
  496.   
  497.     vsprintf(buf, fmt, ap);   
  498.   
  499.     if (errnoflag)   
  500.   
  501.         sprintf(buf + strlen(buf), ": %s", strerror(errno_save));   
  502.   
  503.     strcat(buf, "\n");   
  504.   
  505.     fflush(stdout);   
  506.   
  507.     fputs(buf, stderr);   
  508.   
  509.     fflush(NULL);   
  510.   
  511.     return;   
  512.   
  513. }   
  514.   
  515.   
  516. /* Print a message and terminate. */  
  517.   
  518. void err_quit(const char *fmt, ...){   
  519.   
  520.     va_list ap;   
  521.   
  522.     va_start(ap, fmt);   
  523.   
  524.     err_doit(0, fmt, ap);   
  525.   
  526.     va_end(ap);   
  527.   
  528.     exit(1);   
  529.   
  530. }   
  531.   
PARTNER CONTENT

文章评论3条评论)

登录后参与讨论

用户377235 2016-2-17 16:28

???????????????

用户377235 2015-9-16 04:28

评论是对思考最好的总结…

用户377235 2012-12-16 20:25

哦~~我要交配

用户1199424 2008-3-7 17:33

呵呵, 我来顶你!

用户25706 2007-6-23 10:36

Vishay公司简介

 

 

Vishay是多样化产品的制造商,提供 ”一站式” 服务。它是世界最大的分立半导体和无源电子组件制造商之一。

Vishay 的收购包括这些耳熟能详的著名品牌,如 SiliconixTelefunkenInfineon 的红外线组件业务,此外还有 General SemiconductorDaleDraloricSpragueVitramon BCcomponentsPhilips Electronics Beyschlag 以前的无源组件业务)。这些名称及其相关联的产品已经整合到一家全球公司:Vishay

Vishay 在众多市场都占有一席之位,包括工业、电脑、汽车、消费品、通讯、军事、航空和医学。Vishay 遍布全球各地, 拥有多个制造和销售地点在中国与其它亚洲国家、以色列、欧洲、和美国。亚洲的销售额占了 Vishay 超过三分之一的营业收入。

 

Vishay 的多样化产品 -- 从专业到商品组件 -- 容器、磁性组件、信号转换器、应变测定 (无源组件) 到二极管、整流器、晶体管样、功率IC、模拟开关和路复用器蔼(分立半导体) 到红 外线数据通信设备 (IRDCs)、发光二极管、红外线发射器及检测器和红外线接收器模块 (光电组件)

领先全球的制造商

Vishay 的每样产品具有从强稳到排名第一的市场占有率。

分立半导体

排名第一的低电压 功率MOSFETs

排名第一的整流器

排名第一的玻璃二极管

排名第一的红外线数组件

等等

无源组件

排名第一的线绕功率电阻器

排名第一的箔电阻器

排名第一的薄膜电阻器

排名第一的MELF电阻器

排名第一的引线功率源膜电阻器

排名第一的引线保险丝电阻器

排名第一的湿钽与保形涂层钽电容器

排名第一的铝电容器在欧洲的汽车市场

排名第一的应变测定仪和称重传感器

等等

网站: www.vishay.com

深圳分销商:

深圳市鸿瑞泰电子有限公司

Add: 深圳市福田区中航路都会100大厦B9C

Tel: 0755-82862569 /81219295

Fax: 0755-82866449

电邮:krtic@163.com

用户19934 2007-4-17 12:06

Vishay集团的无源器件包括电阻、无源传感器、电容、电感,半导体器件则包括二极管和各类晶体管、光电子产品、功率IC和模拟开关IC,产品被美国、欧洲和亚洲的许多制造商DELL,IBM,HP,NOKIA, MOTOROLA, SONY,BENZ,FORD,SAMSUNG, GE, BOENG, LOCKHEED MARTIN广泛应用于计算机、电话、电视、汽车、家用器具、医疗仪器、卫星、军事/航空设备领域

用户19934 2007-4-17 12:04

链接地址:http://www.vishay.com/

全线代理vishay公司产品线。

相关推荐阅读
xuyaosong 2012-11-28 14:24
Lesson 4 4:MATLAB - FFT and Zero Padding
http://blinkdagger.com/matlab/matlab-fft-and-zero-padding This is the fourth post in the blinkdag...
xuyaosong 2012-11-28 13:54
C语言中 多个源文件之间函数如何调用
首先要建立一个头文件,以.h保存 这样 #include typedef struct { char name[100][60]; char number[100][8]; int m...
xuyaosong 2012-11-28 13:53
小波变换尺度相关性去噪程序
所实现的相关性去噪函数为function [s1 a d] = SSNF(s, n, h, g, g1),具体的实现步骤为: 1) 调用离散二进小波分解函数对信号进行分解,得到逼近系数a 和细节...
xuyaosong 2012-11-28 13:29
功率谱密度幅值的具体含义??
http://www.chinavib.com/forum/thread-17307-1-48.html 求信号功率谱时候用下面的不同方法,功率谱密度的幅值大小相差很大! 我的问题是,计算具体...
xuyaosong 2012-01-12 18:38
声明函数指针并实现回调
程序员常常需要实现回调。本文将讨论函数指针的基本原则并说明如何使用函数指针实现回调。注意这里针对的是普通的函数,不包括完全依赖于不同语法和语义规则的类成员函数(类成员指针将在另文中讨论)。 ...
xuyaosong 2012-01-12 18:36
如何不用访问地址的方式来编写并口程序 ★★★★★
兄弟我在精华发现了一些关于并口打印的文章,但是都是用inb outb操作的,不知道各路高人能否给小弟一个用open write ioctl read close控制的判断状态并读写的例子,尤...
EE直播间
更多
我要评论
3
5
关闭 站长推荐上一条 /3 下一条