[Skip Header and Navigation] [Jump to Main Content]
bobah.net
details matter

Primary Links

  • Success Story
  • D4D
  • Tools
  • Source Code
    • Copyright & License
    • Math
    • Networking
      • SSL Client Socket
      • SSL Server Socket
      • TCP Client (Java NIO)
    • Miscelanea
  • Process
  • Raspberry Pi
  • Unix
Home » Developer for Developers » Source Code » Networking

SSL Client Socket

Submitted by bobah on April 12, 2010 - 21:31
A minimal SSL client (also a TCP client). Demonstrates TCP and SSL client socket creation, initialization and usage by means of pure POSIX API.
 #include <openssl/ssl.h>
 #include <openssl/bio.h>
 #include <openssl/err.h>

 #include <cstdio>
 #include <cstring>

 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/types.h>
 #include <errno.h>
 #include <assert.h>

 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
 #include <netdb.h>
 #include <sys/time.h>
 #include <string.h>

 #include <iostream>
 #include <iomanip>


 namespace {

 enum {
   FATAL = 0,
   ERROR,
   WARNING,
   INFO,
   DETAIL,
   DEBUG,
   TRACE
 };

 int _logLevel = DETAIL;

 #define LOG(level,msg) do { if (level <= _logLevel) {std::cout << msg; } } while(0)
 #define LOG_ERROR(msg) do { LOG(ERROR, "-E- " << __func__ << ' ' << msg << std::endl); } while(0)
 #define LOG_DEBUG(msg) do { LOG(DEBUG, "-D- " << __func__ << ' ' << msg << '\n'); } while(0)
 #define LOG_INFO(msg)  do { LOG(INFO,  "-I- " << __func__ << ' ' << msg << '\n'); } while(0)

 #define PERROR_AND_RETURN(rc) do {                                           \
   LOG_ERROR(strerror(errno) << '(' << errno << ')'); \
   return rc;                                                                        \
 } while (0)


 struct addrinfo* get_addrinfo(const char* node, const char* service, bool tcp) {
   struct addrinfo  hints  = addrinfo();
   struct addrinfo* result = NULL;

   memset(&hints, 0, sizeof(struct addrinfo));
   hints.ai_family    = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
   hints.ai_socktype  = tcp ? SOCK_STREAM : SOCK_DGRAM; /* Stream/Datagram socket */
   hints.ai_flags     = 0;
   hints.ai_protocol  = 0;          /* Any protocol */

   int rc = 0;
   if (getaddrinfo(node, service, &hints, &result) != 0) { LOG_ERROR(gai_strerror(rc)); return NULL; }

   return result;
 }

 /**
  * 
  */
 int configure_socket(int fd, const struct addrinfo& ai, unsigned sndbuf, unsigned rcvbuf, bool blocking, bool nolinger, bool nodelay) {
   if (nolinger) {
     /* Set the socket for a non lingering, graceful close.
      * This will cause a final close of this socket not to wait until all
      * of the data sent on it has been received by the remote host.
      * The result is that the socket will be immediately released instead
      * of blocking in a TIME_WAIT state */
     linger linger = {1, 0};
     if (::setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)) == -1) { LOG_ERROR(strerror(errno)); return -1; }
   }

   if (nodelay) {
     int flag = 1;
     if (::setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)) == -1) { LOG_ERROR(strerror(errno)); return -1; }
   }

   if (sndbuf > 0 && setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) == -1) { LOG_ERROR(strerror(errno)); return -1; }
   if (rcvbuf > 0 && setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) == -1) { LOG_ERROR(strerror(errno)); return -1; }

   // put socket to blocking/non-blocking mode
   int flags = -1;
   if ((flags = fcntl(fd, F_GETFL, 0)) == -1) { LOG_ERROR(strerror(errno)); return -1; }
   if (blocking) flags &= ~O_NONBLOCK;
   else          flags |= O_NONBLOCK;
   if (fcntl(fd, F_SETFL, flags) == -1) { LOG_ERROR(strerror(errno)); return -1; }

   return 0;
 }


 /**
  * returns tcp-connected socket
  */
 int get_tcp_connection(const char* node, const char* service) {
   struct addrinfo* ai = get_addrinfo(node, service, true /* tcp */);
   if (ai == NULL) return -1;

   const unsigned sndbuf = 1024*128;
   const unsigned rcvbuf = 1024*128;

   int fd = -1;
   for (struct addrinfo* rp = ai; rp != NULL; rp = rp->ai_next) {
     if ((fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1) { LOG_ERROR(strerror(errno)); fd = -1; break; }
     if (configure_socket(fd, *rp, sndbuf, rcvbuf, true /* blocking */, true /* nolinger */, true /* nodelay */) != 0) { ::close(fd); fd = -1; break; }

     if (::connect(fd, rp->ai_addr, rp->ai_addrlen) != -1) break;
     ::close(fd); fd = -1;
   }

   ::freeaddrinfo(ai);
   return fd;
 }

 }

 int main()
 {
     int p;

     const char* request = "GET /\n\n";
     char r[1024];

     /* Set up the library */
     SSL_library_init();
     ERR_load_BIO_strings();
     SSL_load_error_strings();
     OpenSSL_add_all_algorithms();

     /* Set up the SSL context */

     SSL_CTX* ctx = SSL_CTX_new(SSLv3_method());
     if (!ctx) {
       ERR_print_errors_fp(stderr);
       return 1;
     }

     /* Load the trust store */

     if(! SSL_CTX_load_verify_locations(ctx, NULL, "/etc/ssl/certs"))
     {
         fprintf(stderr, "Error loading trust store\n");
         ERR_print_errors_fp(stderr);
         SSL_CTX_free(ctx);
         return 0;
     }

     /* Setup the connection */

     int fd = get_tcp_connection("127.0.0.1", "12345");
 //    int fd = my_connect("127.0.0.1", 80);
     if (fd == -1) {
       LOG_ERROR(strerror(errno));
       return 1;
     }


     SSL* ssl = SSL_new(ctx); assert(ssl);
     if (!SSL_set_fd(ssl, fd)) {
       LOG_ERROR("SSL_set_fd(ssl, fd)");
       return 1;
     }

     SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

     int rc = SSL_get_error(ssl, SSL_connect(ssl));
     if (rc != SSL_ERROR_NONE) {
       LOG_ERROR(ERR_error_string(rc, NULL));
       while ((rc = ERR_get_error()) != 0) {
         LOG_ERROR(ERR_error_string(rc, NULL));
       }
       return 1;
     }

     /* Check the certificate */

     rc = SSL_get_verify_result(ssl);
     if(rc != X509_V_OK)
     {
         if (rc == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || rc == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) {
           fprintf(stderr, "self signed certificate\n");
         }
         else {
           fprintf(stderr, "Certificate verification error: %ld\n", SSL_get_verify_result(ssl));
           SSL_CTX_free(ctx);
           return 0;
         }
     }

     /* Send the request */
     if (SSL_write(ssl, request, strlen(request)) == -1) {
       PERROR_AND_RETURN(1);
     }
     LOG_INFO("Data sent [" << request << "]");

     /* Read in the response */

     for(;;)
     {
         p = SSL_read(ssl, r, 1023);
         if(p <= 0) break;
         r[p] = 0;
         printf("%s", r);
     }


     rc = SSL_shutdown(ssl);
     if(!rc){
       /* If we called SSL_shutdown() first then
          we always get return value of '0'. In
          this case, try again, but first send a
          TCP FIN to trigger the other side's
          close_notify*/
       shutdown(fd,1);
       rc = SSL_shutdown(ssl);
     }
     switch(rc){
       case 1:
         break; /* Success */
       case 0:
       case -1:
       default:
         LOG_ERROR("Shutdown failed");
     }

     /* Close the connection and free the context */

     SSL_CTX_free(ctx);
 //     if (write(fd, request, strlen(request)) == -1) {
 //       PERROR_AND_RETURN("write(fd, request, strlen(request))", 1);
 //     }
 // 
 //     /* Read in the response */
 // 
 //     for(;;)
 //     {
 //         p = read(fd, r, 1023);
 //         if(p <= 0) break;
 //         r[p] = 0;
 //         printf("%s", r);
 //     }
     return 0;
 }
‹ Networking up SSL Server Socket ›
  • Printer-friendly version
  • Add new comment
© 2008—2011 bobah.net
[Jump to Top] [Jump to Main Content]