-
- /* Compile with: gcc -o simple_client2 simple_client2.c -std=gnu99 -lssl -lcrypto -ldl */
-
- #include <openssl/ssl.h>
- #include <openssl/bio.h>
- #include <openssl/err.h>
-
- #include <stdio.h>
- #include <string.h>
-
- #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 <stdbool.h>
-
- // #include <iostream.h>
- // #include <iomanip.h>
-
- enum {
- FATAL = 0,
- ERROR,
- WARNING,
- INFO,
- DETAIL,
- DEBUG,
- TRACE
- };
-
- int _logLevel = DETAIL;
-
- #define LOG(level,msg) printf("LOG: %s\n", msg);
- #define LOG_ERROR(msg) printf("LOG ERR: %s\n", msg); //do { LOG(ERROR, "-E- " << __func__ << ' ' << msg << std::endl); } while(0)
- #define LOG_DEBUG(msg) printf("LOG DBG: %s\n", msg); //do { LOG(DEBUG, "-D- " << __func__ << ' ' << msg << '\n'); } while(0)
- #define LOG_INFO(msg) printf("LOG INF: %s\n", msg); //;do { LOG(INFO, "-I- " << __func__ << ' ' << msg << '\n'); } while(0)
-
- #define PERROR_AND_RETURN(rc) do { \
- LOG_ERROR(strerror(errno)); \
- return rc; \
- } while (0)
-
-
- struct addrinfo* get_addrinfo(const char* node, const char* service, bool tcp) {
- struct addrinfo hints;
- 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, NULL/*&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 */
- int 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 */, false /* 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;
- request = "POST /index HTTP/1.1\r\n"
- "Host: foo.com\r\n"
- "Content-Length: 5"
- "\r\n\r\n" "part3";
- 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(SSLv23_method());
- if (!ctx) {
- ERR_print_errors_fp(stderr);
- return 1;
- }
-
- SSL_CTX_set_session_id_context(ctx, "foobarbaz", 10);
-
- /* Load the trust store */
- #if 0
- 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;
- }
- #endif
-
- /* Setup the connection */
-
- int fd = get_tcp_connection("127.0.0.1", "5555");
- // 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 */
- #if 0
- 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;
- }
- }
- #endif
-
- /* Send the request */
- request = "POST /index HTTP/1.1\r\n"
- "Host: foo.com\r\n"
- "Content-Length: 5"
- "\r\n\r\n" "part1";
- if (SSL_write(ssl, request, strlen(request)) == -1) {
- PERROR_AND_RETURN(1);
- }
- LOG_INFO("Data sent [");
- LOG_INFO(request);
- LOG_INFO("]");
-
- /* Read in the response */
- // for(;;)
- // {
- p = SSL_read(ssl, r, 1023);
- //if(p <= 0) break;
- r[p] = 0;
- printf("%s", r);
- // }
-
- /* Send the request */
- request = "POST /index HTTP/1.1\r\n"
- "Host: foo.com\r\n"
- "Content-Length: 5"
- "\r\n\r\n" "part2";
- if (SSL_write(ssl, request, strlen(request)) == -1) {
- PERROR_AND_RETURN(1);
- }
- LOG_INFO("Data sent [");
- LOG_INFO(request);
- LOG_INFO("]");
-
- /* Read in the response */
- // for(;;)
- // {
- p = SSL_read(ssl, r, 1023);
- //if(p <= 0) break;
- r[p] = 0;
- printf("%s", r);
- // }
- SSL_renegotiate_abbreviated(ssl);
- while(!SSL_do_handshake(ssl));
- SSL_heartbeat(ssl);
-
- /* Send the request */
- request = "POST /index HTTP/1.1\r\n"
- "Host: foo.com\r\n"
- "Content-Length: 5"
- "\r\n\r\n" "part3";
- if (SSL_write(ssl, request, strlen(request)) == -1) {
- PERROR_AND_RETURN(1);
- }
- LOG_INFO("Data sent [");
- LOG_INFO(request);
- LOG_INFO("]");
-
- /* Read in the response */
- // for(;;)
- // {
- p = -1;
- while (p < 0) {
- p = SSL_read(ssl, r, 1023);
- //if(p <= 0) break;
- printf("Received %d bytes\n", p);
- if (p >= 0) {
- r[p] = 0;
- printf("%s", r);
- }
- }
- // }
-
- exit(0);
-
- #if 0
-
- 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;
- #endif
- }
-