Skip to content

Instantly share code, notes, and snippets.

@s8sg
Created January 31, 2018 03:09
Show Gist options
  • Select an option

  • Save s8sg/55768431bee58d79e863060777136039 to your computer and use it in GitHub Desktop.

Select an option

Save s8sg/55768431bee58d79e863060777136039 to your computer and use it in GitHub Desktop.
a sample code to parse the protocol proxy header (v1/v2)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUFSIZE 1024
#if 0
/*
* Structs exported from in.h
* */
/* Internet address */
struct in_addr {
unsigned int s_addr;
};
/* Internet style socket address */
struct sockaddr_in {
unsigned short int sin_family; /* Address family */
unsigned short int sin_port; /* Port number */
struct in_addr sin_addr; /* IP address */
unsigned char sin_zero[...]; /* Pad to size of 'struct sockaddr' */
};
/*
* Struct exported from netdb.h
*/
/* Domain name service (DNS) host entry */
struct hostent {
char *h_name; /* official name of host */
char **h_aliases; /* alias list */
int h_addrtype; /* host address type */
int h_length; /* length of address */
char **h_addr_list; /* list of addresses */
}
#endif
/*
* error - wrapper for perror
*/
void error(char *msg) {
perror(msg);
exit(1);
}
struct sockaddr_storage from; /* already filled by accept() */
struct sockaddr_storage to; /* already filled by getsockname() */
const char v2sig[12] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
const char *reply = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"
"<html xmlns=\"http://www.w3.org/1999/xhtml\">"
"<head>"
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"
"<title>Protocol Buffer Test Passed</title>\"";
/* returns 0 if needs to poll, <0 upon error or >0 if it did the job */
int read_evt(int fd)
{
union {
struct {
char line[108];
} v1;
struct {
uint8_t sig[12];
uint8_t ver_cmd;
uint8_t fam;
uint16_t len;
union {
struct { /* for TCP/UDP over IPv4, len = 12 */
uint32_t src_addr;
uint32_t dst_addr;
uint16_t src_port;
uint16_t dst_port;
} ip4;
struct { /* for TCP/UDP over IPv6, len = 36 */
uint8_t src_addr[16];
uint8_t dst_addr[16];
uint16_t src_port;
uint16_t dst_port;
} ip6;
struct { /* for AF_UNIX sockets, len = 216 */
uint8_t src_addr[108];
uint8_t dst_addr[108];
} unx;
} addr;
} v2;
} hdr;
int size, ret;
do {
ret = recv(fd, &hdr, sizeof(hdr), MSG_PEEK);
} while (ret == -1 && errno == EINTR);
if (ret == -1)
return (errno == EAGAIN) ? 0 : -1;
if (ret >= 16 && memcmp(&hdr.v2, v2sig, 12) == 0 &&
(hdr.v2.ver_cmd & 0xF0) == 0x20) {
printf("V2 signature match\n");
size = 16 + ntohs(hdr.v2.len);
printf("size: %d len: %d\n", size, ntohs(hdr.v2.len));
if (ret < size)
return -1; /* truncated or too large header */
switch (hdr.v2.ver_cmd & 0xF) {
case 0x01: /* PROXY command */
switch (hdr.v2.fam) {
case 0x11: /* TCPv4 */
((struct sockaddr_in *)&from)->sin_family = AF_INET;
((struct sockaddr_in *)&from)->sin_addr.s_addr =
hdr.v2.addr.ip4.src_addr;
((struct sockaddr_in *)&from)->sin_port =
hdr.v2.addr.ip4.src_port;
((struct sockaddr_in *)&to)->sin_family = AF_INET;
((struct sockaddr_in *)&to)->sin_addr.s_addr =
hdr.v2.addr.ip4.dst_addr;
((struct sockaddr_in *)&to)->sin_port =
hdr.v2.addr.ip4.dst_port;
printf("TCPv4 v2\n");
goto done;
case 0x21: /* TCPv6 */
((struct sockaddr_in6 *)&from)->sin6_family = AF_INET6;
memcpy(&((struct sockaddr_in6 *)&from)->sin6_addr,
hdr.v2.addr.ip6.src_addr, 16);
((struct sockaddr_in6 *)&from)->sin6_port =
hdr.v2.addr.ip6.src_port;
((struct sockaddr_in6 *)&to)->sin6_family = AF_INET6;
memcpy(&((struct sockaddr_in6 *)&to)->sin6_addr,
hdr.v2.addr.ip6.dst_addr, 16);
((struct sockaddr_in6 *)&to)->sin6_port =
hdr.v2.addr.ip6.dst_port;
printf("TCPv6 v2\n");
goto done;
default:
printf("Invalid family: %x\n", hdr.v2.fam);
}
/* unsupported protocol, keep local connection address */
break;
case 0x00: /* LOCAL command */
/* keep local connection address for LOCAL */
break;
default:
printf("Invalid command: %x -> %x\n", hdr.v2.ver_cmd, hdr.v2.ver_cmd & 0xF);
return -1; /* not a supported command */
}
}
else if (ret >= 8 && memcmp(hdr.v1.line, "PROXY", 5) == 0) {
char *end = memchr(hdr.v1.line, '\r', ret - 1);
if (!end || end[1] != '\n')
return -1; /* partial or invalid header */
*end = '\0'; /* terminate the string to ease parsing */
size = end + 2 - hdr.v1.line; /* skip header + CRLF */
/* parse the V1 header using favorite address parsers like inet_pton.
* return -1 upon error, or simply fall through to accept.
*/
printf("TCPv4/v6 v1\n");
}
else {
/* Wrong protocol */
return -1;
}
done:
/* we need to consume the appropriate amount of data from the socket */
do {
ret = recv(fd, &hdr, size, 0);
} while (ret == -1 && errno == EINTR);
return (ret >= 0) ? 1 : -1;
}
int main(int argc, char **argv) {
int parentfd; /* parent socket */
int childfd; /* child socket */
int portno; /* port to listen on */
int clientlen; /* byte size of client's address */
struct sockaddr_in serveraddr; /* server's addr */
struct sockaddr_in clientaddr; /* client addr */
struct hostent *hostp; /* client host info */
char buf[BUFSIZE]; /* message buffer */
char *hostaddrp; /* dotted decimal host addr string */
int optval; /* flag value for setsockopt */
int n; /* message byte size */
/*
* check command line arguments
*/
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(1);
}
portno = atoi(argv[1]);
/*
* socket: create the parent socket
*/
parentfd = socket(AF_INET, SOCK_STREAM, 0);
if (parentfd < 0)
error("ERROR opening socket");
/* setsockopt: Handy debugging trick that lets
* us rerun the server immediately after we kill it;
* otherwise we have to wait about 20 secs.
* Eliminates "ERROR on binding: Address already in use" error.
*/
optval = 1;
setsockopt(parentfd, SOL_SOCKET, SO_REUSEADDR,
(const void *)&optval , sizeof(int));
/*
* build the server's Internet address
*/
bzero((char *) &serveraddr, sizeof(serveraddr));
/* this is an Internet address */
serveraddr.sin_family = AF_INET;
/* let the system figure out our IP address */
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
/* this is the port we will listen on */
serveraddr.sin_port = htons((unsigned short)portno);
/*
* bind: associate the parent socket with a port
*/
if (bind(parentfd, (struct sockaddr *) &serveraddr,
sizeof(serveraddr)) < 0)
error("ERROR on binding");
/*
* listen: make this socket ready to accept connection requests
*/
if (listen(parentfd, 5) < 0) /* allow 5 requests to queue up */
error("ERROR on listen");
/*
* main loop: wait for a connection request, echo input line,
* then close connection.
*/
clientlen = sizeof(clientaddr);
while (1) {
/*
* accept: wait for a connection request
*/
childfd = accept(parentfd, (struct sockaddr *) &clientaddr, &clientlen);
if (childfd < 0)
error("ERROR on accept");
/*
* gethostbyaddr: determine who sent the message
*/
hostp = gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
if (hostp == NULL)
error("ERROR on gethostbyaddr");
hostaddrp = inet_ntoa(clientaddr.sin_addr);
if (hostaddrp == NULL)
error("ERROR on inet_ntoa\n");
printf("server established connection with %s (%s)\n",
hostp->h_name, hostaddrp);
/*
* read: read input string from the client
*/
n = read_evt(childfd);
if (n < 0) {
printf("Failed to parse header\n");
close(childfd);
continue;
}
/*
* write: echo the input string back to the client
*/
bzero(buf, BUFSIZE);
memcpy(buf, reply, strlen(reply) + 1);
n = write(childfd, buf, strlen(buf));
if (n < 0)
error("ERROR writing to socket");
close(childfd);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment