Chinaunix首页 | 论坛 | 博客
  • 博客访问: 752010
  • 博文数量: 119
  • 博客积分: 137
  • 博客等级: 少校
  • 技术积分: 1582
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-28 16:39
文章分类

全部博文(119)

文章存档

2017年(3)

2016年(7)

2014年(1)

2013年(8)

2012年(20)

2011年(27)

2010年(53)

分类: LINUX

2012-06-12 09:14:38

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

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

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

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

源代码如下:

点击(此处)折叠或打开

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

阅读(8474) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~