File: tcp-probe.c

package info (click to toggle)
sslh 2.1.4-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 1,960 kB
  • sloc: ansic: 7,681; perl: 683; sh: 356; makefile: 136
file content (99 lines) | stat: -rw-r--r-- 3,078 bytes parent folder | download
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
/*
# tcp-probe.c: TCP code that is common to the sslh-fork and sslh-[ev|select] 
#
# Copyright (C) 2022  Yves Rutschle
# 
# This program is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later
# version.
# 
# This program is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE.  See the GNU General Public License for more
# details.
# 
# The full text for the General Public License is here:
# http://www.gnu.org/licenses/gpl.html
*/


#include "probe.h"

static struct sslhcfg_protocols_item** tcp_protocols;
static int tcp_protocols_len = 0;

/*
 * Read the beginning of data coming from the client connection and check if
 * it's a known protocol.
 * Return PROBE_AGAIN if not enough data, or PROBE_MATCH if it succeeded in
 * which case cnx->proto is set to the appropriate protocol.
 */
int probe_client_protocol(struct connection *cnx)
{
    char buffer[BUFSIZ];
    ssize_t n;

    n = read(cnx->q[0].fd, buffer, sizeof(buffer));
    /* It's possible that read() returns an error, e.g. if the client
     * disconnected between the previous call to select() and now. If that
     * happens, we just connect to the default protocol so the caller of this
     * function does not have to deal with a specific  failure condition (the
     * connection will just fail later normally). */

    if (n > 0) {
        defer_write(&cnx->q[1], buffer, n);
        return probe_buffer(cnx->q[1].begin_deferred_data,
                            cnx->q[1].deferred_data_size,
                            tcp_protocols, tcp_protocols_len,
                            &cnx->proto
                            );
    }

    /* read() returned an error, so just connect to the last protocol to die */
    cnx->proto = &cfg.protocols[cfg.protocols_len-1];
    return PROBE_MATCH;
}


static void tcp_protocol_list_init(void)
{
    tcp_protocols = calloc(cfg.protocols_len, sizeof(tcp_protocols));
    CHECK_ALLOC(tcp_protocols, "tcp_protocols");
    for (int i = 0; i < cfg.protocols_len; i++) {
        struct sslhcfg_protocols_item* p = &cfg.protocols[i];
        if (!p->is_udp) {
            tcp_protocols[tcp_protocols_len] = p;
            tcp_protocols_len++;
        }
    }
}

/* Configuration sanity check for TCP:
 * - If there is a listening socket, there must be at least one target
 */
static void tcp_sanity_check(void)
{
    int tcp_present = 0;

    for (int i = 0; i < cfg.listen_len; i++) {
        struct sslhcfg_listen_item* p = &cfg.listen[i];
        if (!p->is_udp) {
            tcp_present = 1;
            break;
        }
    }

    if (tcp_present && !tcp_protocols_len) {
        print_message(msg_config_error, "At least one TCP target protocol must be specified.\n");
        exit(2);
    }
}

void tcp_init(void)
{
    tcp_protocol_list_init();
    tcp_sanity_check();
}