Blob


1 /*
2 * Copyright (c) 2024 github.com/Sir-Photch <sir-photch@posteo.me>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include "gmid.h"
19 #include <stdint.h>
20 #include <string.h>
22 #define MIN(a, b) (a) < (b) ? (a) : (b)
24 static int
25 check_prefix_v1(char **buf)
26 {
27 static const char PROXY[6] = "PROXY ";
29 if (strncmp(*buf, PROXY, sizeof(PROXY)) != 0)
30 return (-1);
32 *buf += sizeof(PROXY);
34 return (0);
35 }
37 static int
38 check_proto_v1(char **buf)
39 {
40 static const char TCP[3] = "TCP";
42 if (strncmp(*buf, TCP, sizeof(TCP)) != 0)
43 return (-1);
45 *buf += sizeof(TCP);
47 int type;
48 switch ((*buf)[0]) {
49 case '4': type = 4; break;
50 case '6': type = 6; break;
51 default: return (-1);
52 }
54 if ((*buf)[1] != ' ')
55 return (-1);
57 // '4' / '6' + ' '
58 *buf += 2;
60 return type;
61 }
63 static int
64 check_unknown_v1(char **buf)
65 {
66 static const char UNKNOWN[7] = "UNKNOWN";
68 if (strncmp(*buf, UNKNOWN, sizeof(UNKNOWN)) != 0)
69 return (-1);
71 *buf += sizeof(UNKNOWN);
73 return (0);
74 }
76 static int
77 check_crlf_v1(char *const *buf, size_t buflen)
78 {
79 static const char CRLF[2] = "\r\n";
81 if (buflen < sizeof(CRLF))
82 return (-1);
84 if (!memmem(*buf, buflen, CRLF, sizeof(CRLF)))
85 return (-1);
87 return (0);
88 }
90 static int
91 check_ip_v1(int af, void *addr, char **buf)
92 {
93 char *spc;
95 if ((spc = strchr(*buf, ' ')) == NULL)
96 return (-1);
98 *spc++ = '\0';
100 if (inet_pton(af, *buf, addr) != 1)
101 return (-1);
103 *buf = spc;
105 return (0);
108 static int
109 check_port_v1(uint16_t *port, char **buf, size_t *buflen)
111 size_t wspc_idx = strcspn(*buf, " \r");
112 char *wspc = *buf + wspc_idx;
114 if (!(*wspc == ' ' || *wspc == '\r'))
115 return (-1);
117 *wspc++ = '\0';
119 const char *errstr;
120 long long num = strtonum(*buf, 0, UINT16_MAX, &errstr);
121 if (errstr)
122 return (-1);
124 *buf = wspc;
125 *port = num;
127 return (0);
130 int
131 proxy_proto_v1_parse(struct proxy_protocol_v1 *s, char *buf, size_t buflen,
132 size_t *consumed)
134 const char *begin = buf;
136 if (check_crlf_v1(&buf, buflen) == -1 ||
137 check_prefix_v1(&buf) == -1)
138 return (-1);
140 switch (check_proto_v1(&buf)) {
141 case 4: s->proto = PROTO_V4; break;
142 case 6: s->proto = PROTO_V6; break;
143 case -1:
144 if (check_unknown_v1(&buf) == -1)
145 return (-1);
146 s->proto = PROTO_UNKNOWN;
147 return (0);
148 default:
149 return (-1);
152 switch (s->proto) {
153 case PROTO_V4:
154 if (check_ip_v1(AF_INET, &s->srcaddr.v4, &buf) == -1 ||
155 check_ip_v1(AF_INET, &s->dstaddr.v4, &buf) == -1)
156 return (-1);
157 break;
159 case PROTO_V6:
160 if (check_ip_v1(AF_INET6, &s->srcaddr.v6, &buf) == -1 ||
161 check_ip_v1(AF_INET6, &s->dstaddr.v6, &buf) == -1)
162 return (-1);
163 break;
165 default:
166 return (-1);
169 if (check_port_v1(&s->srcport, &buf, &buflen) == -1 ||
170 check_port_v1(&s->dstport, &buf, &buflen) == -1)
171 return (-1);
173 if (*buf != '\n')
174 return (-1);
175 buf += 1;
177 *consumed = buf - begin;
178 return (0);
181 int
182 proxy_proto_v1_string(const struct proxy_protocol_v1 *s, char *buf,
183 size_t buflen)
185 // "0000:0000:0000:0000:0000:0000:0000:0000\0"
186 char srcaddrbuf[40], dstaddrbuf[40];
187 int ret;
188 switch (s->proto) {
189 case PROTO_UNKNOWN:
190 ret = snprintf(buf, buflen, "unknown");
191 goto fin;
192 case PROTO_V4:
193 inet_ntop(AF_INET, &s->srcaddr.v4, srcaddrbuf,
194 sizeof(srcaddrbuf));
195 inet_ntop(AF_INET, &s->dstaddr.v4, dstaddrbuf,
196 sizeof(dstaddrbuf));
197 break;
198 case PROTO_V6:
199 inet_ntop(AF_INET6, &s->srcaddr.v6, srcaddrbuf,
200 sizeof(srcaddrbuf));
201 inet_ntop(AF_INET6, &s->dstaddr.v6, dstaddrbuf,
202 sizeof(dstaddrbuf));
203 break;
206 ret = snprintf(
207 buf,
208 buflen,
209 "from %s port %u via %s port %u",
210 srcaddrbuf,
211 s->srcport,
212 dstaddrbuf,
213 s->dstport);
215 fin:
216 return ret;