aboutsummaryrefslogtreecommitdiff
path: root/src/molerat/connect.c
blob: 96c904b7b86c35e6349fdeddfd2a6e12da0664f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include <errno.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <stdbool.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include "connect.h"
#include "status.h"
#include "url.h"

int connect_socket(struct config *conf, struct url url) {
  struct addrinfo hints;
  struct addrinfo *servinfo;

  memset(&hints, 0, sizeof hints);
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = AI_PASSIVE;

  char port[5];
  sprintf(port, "%d", url.port);
  int rc = getaddrinfo(url.host, port, &hints, &servinfo);
  if (rc != 0) {
    error_status(conf, (char *)gai_strerror(rc));
    return ERR_GETADDRINFO;
  }

  struct addrinfo *p;
  int sockfd;
  for (p = servinfo; p != NULL; p = p->ai_next) {
    sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
    if (sockfd == -1) {
      continue;
    }

    rc = connect(sockfd, p->ai_addr, p->ai_addrlen);
    if (rc == -1) {
      close(sockfd);
      continue;
    }

    break;
  }

  if (p == NULL) {
    error_status(conf, strerror(errno));
    return ERR_CONNECT;
  }
  update_status(conf, "Connected to socket");
  freeaddrinfo(servinfo);

  return sockfd;
}

int tls_connect(struct config *conf, struct url url) {
  char status_msg[strlen(url.host) + 128];
  sprintf(status_msg, "Connecting to %s...", url.host);
  update_status(conf, status_msg);

  struct connection *conn = init_connection();
  conf->s.conn = conn;

  int sock;
  int rc = connect_socket(conf, url);
  if (rc < 0) {
    return rc;
  } else {
    sock = rc;
  }

  const SSL_METHOD *method;
  method = TLS_method();

  SSL_CTX *ctx;
  ctx = SSL_CTX_new(method);
  if (ctx == NULL) {
    error_status(conf, "Failed to init SSL");
    return ERR_SSL_CTX;
  }

  SSL *ssl;
  ssl = SSL_new(ctx);
  if (ssl == NULL) {
    error_status(conf, "Failed to init SSL");
    return ERR_SSL_SSL;
  }

  rc = SSL_set_fd(ssl, sock);
  if (rc == 0) {
    error_status(conf, "Failed to wrap socket");
    return ERR_SSL_SSL;
  }

  rc = SSL_connect(ssl);
  if (rc <= 0) {
    error_status(conf, "Failed to connect SSL");
    return ERR_SSL_SSL;
  }

  conn->ssl = ssl;
  conn->sockfd = sock;
  conn->used = true;
  conf->s.conn = conn;

  update_status(conf, "Connected");

  return 0;
}

void tls_cleanup(struct connection *conn) {
  if (conn->sockfd) {
    shutdown(conn->sockfd, SHUT_RDWR);
    close(conn->sockfd);
  }

  if (conn->ssl) {
    SSL_shutdown(conn->ssl);
    SSL_CTX_free(SSL_get_SSL_CTX(conn->ssl));
    SSL_free(conn->ssl);
  }

  free(conn);
}

struct connection *init_connection(void) {
  struct connection *conn = malloc(sizeof(struct connection));
  memset(conn, 0, sizeof(struct connection));

  return conn;
}