Add socks5 authentication, UDP config, cmd (parser) and client

Co-authored-by: Ezequiel Bellver <ebellver@itba.edu.ar>
Co-authored-by: Juan Barmasch <jbarmasch@itba.edu.ar>
This commit is contained in:
Santiago Lo Coco 2022-06-14 15:57:53 -03:00
parent bbe23ecbd6
commit 23ee49e49f
20 changed files with 1680 additions and 188 deletions

1
.gitignore vendored
View File

@ -12,6 +12,7 @@ test/
## Output
socks5d
client
*.o
a.out

View File

@ -1,12 +1,17 @@
CCFLAGS = -std=c11 -Wall -Wextra -Wno-unused-parameter -Wno-implicit-fallthrough -pedantic -pedantic-errors -fsanitize=address -g -D_POSIX_C_SOURCE=200809L
LDFLAGS = -lpthread
CCFLAGS = -std=c11 -Wall -Wextra -Wno-unused-parameter -Wno-implicit-fallthrough -pedantic -pedantic-errors -fsanitize=address -fno-omit-frame-pointer -g -D_POSIX_C_SOURCE=200809L
LDFLAGS = -lpthread -lm
SERVER_SOURCES=src/args.c src/selector.c src/socks5nio.c src/stm.c src/hello.c src/request.c src/buffer.c src/server.c
# SERVER_SOURCES=src/args.c src/selector.c src/socks5nio.c src/confignio.c src/stm.c src/hello.c src/request.c src/buffer.c src/server.c
SERVER_SOURCES=src/args.c src/selector.c src/socks5nio.c src/confignio.c src/stm.c src/hello.c src/request.c src/buffer.c src/server.c src/auth.c src/cmd.c
CLIENT_SOURCES=src/client.c
SERVER_LIBS=include/args.h include/selector.h include/socks5nio.h include/stm.h include/hello.h include/request.h include/buffer.h include/server.h
CLIENT_LIBS=
SERVER_OBJECTS=$(SERVER_SOURCES:.c=.o)
CLIENT_OBJECTS=$(CLIENT_SOURCES:.c=.o)
SERVER_TARGET=socks5d
CLIENT_TARGET=client
all: $(SERVER_OBJECTS) $(SERVER_TARGET)
all: $(SERVER_OBJECTS) $(SERVER_TARGET) $(CLIENT_OBJECTS) $(CLIENT_TARGET)
%.o : %.c
$(CC) $(CCFLAGS) $(LDFLAGS) -I ./include -c $< -o $@
@ -14,8 +19,11 @@ all: $(SERVER_OBJECTS) $(SERVER_TARGET)
$(SERVER_TARGET): $(SERVER_OBJECTS)
$(CC) $(CCFLAGS) $(LDFLAGS) -I ./include -o $@ $^
$(CLIENT_TARGET): $(CLIENT_OBJECTS)
$(CC) $(CCFLAGS) $(LDFLAGS) -I ./include -o $@ $^
clean:
rm -f $(SERVER_OBJECTS) $(SERVER_TARGET)
rm -f $(SERVER_OBJECTS) $(SERVER_TARGET) $(CLIENT_OBJECTS) $(CLIENT_TARGET) report.tasks strace_out
test: clean
pvs-studio-analyzer trace -- make
@ -24,7 +32,4 @@ test: clean
rm -f PVS-Studio.log
cppcheck --quiet --enable=all --force --inconclusive -I include .
cleanTest:
rm -f report.tasks strace_out
.PHONY: all clean test cleanTest
.PHONY: all clean test

View File

@ -1,7 +1,3 @@
May 24, 2022
@ -41,31 +37,23 @@ Tabla de Contenidos
El servidor DEBE
1. atender a múltiples clientes en forma concurrente y simultánea
[X] 1. atender a múltiples clientes en forma concurrente y simultánea
(al menos 500).
2. soportar autenticación usuario / contraseña [RFC1929].
[X] 2. soportar autenticación usuario / contraseña [RFC1929].
3. soportar de mínima conexiones salientes a a servicios TCP a
[-] 3. soportar de mínima conexiones salientes a a servicios TCP a
direcciones IPv4, IPV6, o utilizando FQDN que resuelvan
cualquiera de estos tipos de direcciones.
Enunciado [Pag. 1]
Trabajo Especial 2020/2 May 2022
4. ser robusto en cuanto a las opciones de conexión (si se utiliza
[] 4. ser robusto en cuanto a las opciones de conexión (si se utiliza
un FQDN que resuelve a múltiples direcciones IP y una no está
disponible debe intentar con otros).
5. reportar los fallos a los clientes usando toda la potencia del
[] 5. reportar los fallos a los clientes usando toda la potencia del
protocolo.
6. implementar mecanismos que permitan recolectar métricas que
[] 6. implementar mecanismos que permitan recolectar métricas que
ayuden a monitorear la operación del sistema.
A. cantidad de conexiones históricas
@ -80,19 +68,19 @@ Tabla de Contenidos
Las métricas PUEDEN ser volátiles (si se reinicia el servidor las
estadísticas pueden perderse).
7. implementar mecanismos que permitan manejar usuarios cambiar la
[] 7. implementar mecanismos que permitan manejar usuarios cambiar la
configuración del servidor en tiempo de ejecución sin reiniciar
el servidor. Las diferentes implementaciones PUEDEN decidir
disponibilizar otros cambios de ejecución en tiempo de ejecución
de otras configuraciones (memoria utilizada en I/O, timeouts,
etc).
8. implementar un registro de acceso que permitan a un administrador
[] 8. implementar un registro de acceso que permitan a un administrador
entender los accesos de cada uno de los usuarios. Pensar en el
caso de que llega una queja externa y el administrador debe saber
quien fue el que se conectó a cierto sitio web y cuando.
9. monitorear el tráfico y generar un registro de credenciales de
[] 9. monitorear el tráfico y generar un registro de credenciales de
acceso (usuarios y passwords) de forma similar a ettercap por lo
menos para protocolo POP3.
@ -100,19 +88,10 @@ Tabla de Contenidos
Adicionalmente, la implementación DEBE
1. Estar escritos en el lenguaje de programación C, específicamente
[X] 1. Estar escritos en el lenguaje de programación C, específicamente
con la variante C11 (ISO/IEC 9899:2011).
2. Utilizar sockets en modo no bloqueante multiplexada.
Enunciado [Pag. 2]
Trabajo Especial 2020/2 May 2022
[X] 2. Utilizar sockets en modo no bloqueante multiplexada.
3. Tener en cuenta todos los aspectos que hagan a la buena
performance, escalabilidad y disponibilidad del servidor. Se
@ -127,7 +106,7 @@ Tabla de Contenidos
* ¿Cómo se degrada el throughput?
4. Seguir los lineamientos de IEEE Std 1003.1-2008, 2016 Edition /
[X] 4. Seguir los lineamientos de IEEE Std 1003.1-2008, 2016 Edition /
Base definitions / 12. Utility Conventions [1] a menos que se
especifique lo contrario: Esto se refiere a cómo manejar
argumentos de línea de comandos, parámetros, etc
@ -135,14 +114,14 @@ Tabla de Contenidos
5. Deberá documentar detalladamente el protocolo de monitoreo y
configuración e implementar una aplicación cliente.
6. Tanto la aplicación servidor, como la aplicación cliente de
[X] 6. Tanto la aplicación servidor, como la aplicación cliente de
configuración/monitoreo DEBERÁN manejar los argumentos de línea
de comandos de cierta forma uniforme (por ejemplo -c <puerto>
podría especificar el puerto utilizado para el protocolo de
configuración/monitoreo). Los detalles de qué parámetros se
deben manejar será publicado en otro documento.
7. Si bien las programas son pequeños podrá utilizar librerías o
[X] 7. Si bien las programas son pequeños podrá utilizar librerías o
archivos (fragmento de código) desarrollados por terceros siempre
que se cumplan los siguientes requisitos:
@ -162,18 +141,10 @@ Tabla de Contenidos
de esta forma justificando porqué es válido su uso; y el
propósito de su inclusión. En caso de que sea un fragmento de
código debe adjuntarse. Está permitido utilizar código publicado
Enunciado [Pag. 3]
Trabajo Especial 2020/2 May 2022
por los docentes durante la cursada actual, siempre que se
atribuya correctamente.
8. A veces existirán ambigüedades en las especificaciones o
[] 8. A veces existirán ambigüedades en las especificaciones o
múltiples formas en como se puede resolver o implementar un
problema particular. Por ser una materia de ingeniería se espera
que los alumnos tomen decisiones de diseño razonables en estos
@ -185,13 +156,6 @@ Tabla de Contenidos
3. Evaluación
La realización del Trabajo Práctico es una actividad grupal. La
calificación es de carácter grupal; pero si hay evidencias de que un
alumno de un grupo no participó en la elaboración, o éste no puede
defender o demostrar su participación, entonces el alumno no podrá
aprobar el Trabajo Práctico. Se espera transparencia en el
desarrollo del trabajo (entregar el repositorio git).
Cada grupo DEBE entregar todo el material necesario para poder
reproducir el Trabajo Práctico. Como mínimo DEBE contener:
@ -219,13 +183,6 @@ Tabla de Contenidos
9. Instrucciones para la configuración.
Enunciado [Pag. 4]
Trabajo Especial 2020/2 May 2022
10. Ejemplos de configuración y monitoreo.
11. Documento de diseño del proyecto (que ayuden a entender la
@ -272,65 +229,3 @@ Tabla de Contenidos
escrituras parciales.
o Sumar CUATRO puntos de calificación sobre DIEZ puntos posibles.
Enunciado [Pag. 5]
Trabajo Especial 2020/2 May 2022
Se aceptarán entregas tardías entre 0 horas (inclusive) y 24 horas
(exclusivo) luego de la fecha límite de entrega, pero la calificación
no podrá execeder de CUATRO puntos.
4. Referencias
4.1. Normative References
[RFC1928] Leech, M., Ganis, M., Lee, Y., Kuris, R., Koblas, D., and
L. Jones, "SOCKS Protocol Version 5", RFC 1928, March
1996.
[RFC1929] Leech, M., "Username/Password Authentication for SOCKS
V5", RFC 1929, DOI 10.17487/RFC1929, March 1996,
<https://www.rfc-editor.org/info/rfc1929>.
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119, March 1997.
[RFC3778] Taft, E., Pravetz, J., Zilles, S., and L. Masinter, "The
application/pdf Media Type", RFC 3778,
DOI 10.17487/RFC3778, May 2004,
<http://www.rfc-editor.org/info/rfc3778>.
4.2. URIs
[1] https://pubs.opengroup.org/onlinepubs/9699919799/nframe.html
[2] https://opensource.org/licenses
Enunciado [Pag. 6]

107
docs/pruebas.txt Normal file
View File

@ -0,0 +1,107 @@
0. Precondiciones
# Hostname foo apuntando a servidor http
time curl -s http://foo/1.iso|sha256sum
e260921ef5c7bd5ee2a7b2f2f1156af6483014c73984e4cf37f2b6690e0155e5
# En la terminal donde corramos el curl dejamos configuracion para el cliente
export O="--proxy localhost:1080 --proxy-type socks5 --proxy-auth juan:juan --proxy-dns local"
# sin resoluciones ipv4
export http_proxy=socks5://juan:juan@localhost/
# con resoluciones dns
export http_proxy=socks5h://juan:juan@localhost/
# en la terminal donde corramos el server lo dejamos configurado con usuarios
export OPTS="-u juan:juan -u prueba:prueba"
./socks5d $OPTS
1. Casos
1.1 Defaults bindings
Term A $ ./socks5d $OPTS
Term B $ netstat -nlp|grep socks5d
tcp 0 0 0.0.0.0:1080 0.0.0.0:* LISTEN 8082/./socks5d
tcp6 0 0 :::1080 :::* LISTEN 8082/./socks5d
sctp 127.0.0.1:8080 LISTEN 8082/./socks5d
1.2 Cambio de bindings
Term A $ ./socks5d -p1111 -P 9091 -l ::1 -L0.0.0.0
Term B $ netstat -nlp|grep socks5d
tcp6 0 0 ::1:1111 :::* LISTEN 8235/./socks5d
sctp 0.0.0.0:9091 LISTEN 8235/./socks5d
1.3 Proxy una conexión por vez
Medir caso base $ time curl http://foo/1.iso|md5sum
Term B $ curl http://foo/1.iso|md5sum
Verificar integridad y velocidad
1.4 Proxy 3 conexiones
Term B $ curl http://foo/1.iso|md5sum
Term C $ curl http://foo/1.iso|md5sum
Term D $ curl http://foo/1.iso|md5sum
1.5 Desconexión repentina cliente
Durante la transferencia matar curl
$ curl http://foo/1.iso|md5sum
verificar que no se queda colgado. top.
1.6 Desconexion repentina server
Durante la transferencia matar curl
$ curl http://foo/1.iso|md5sum
$ sudo /etc/init.d/nginx stop
1.7 Origin server (IPV4) no presta servicio
$ curl 'http://127.0.0.1:3333'
1.8 Origin server (IPV6) no presta servicio
$ curl 'http://[::1]:3333'
1.9 Falla resolución de nombres
$ curl 'http://xxxxxxxxxxx/'
1.10 Comportamiento origin server resuelve DNS IPV6
$ curl http://ipv6.leak.com.ar/
1.11 Origin server con múltiples direcciones IP (una falla)
$ dig +short tpe.proto.leak.com.ar
240.0.0.1
127.0.0.1
$ curl http://tpe.proto.leak.com.ar/
1.12 Agnostico del protocolo
Term B: stty -icanon && nc -l 9090
Term C: stty -icanon && nc $O localhost 9090
1.13 Probar enviarle http
http_proxy="" curl http://127.0.0.1:1080/1.iso
ó
stty -icanon && nc localhost 1080
1.14 desde el browser
no logrue que el chrome funcione con user pass
estos dos son interesante.
https://http1.golang.org/gophertiles?latency=0
https://http2.golang.org/gophertiles?latency=0
http://www.http2demo.io/
1.15 Password disector
con nc

270
docs/rfc.txt Normal file
View File

@ -0,0 +1,270 @@
Bottler Protocol version 1
1. Introduction
The ability to get statistics or configure a certain server is
imperative, this protocol provides a way to do it remotely.
Note:
Unless otherwise noted, the decimal numbers appearing in packet-
format diagrams represent the length of the corresponding field, in
octets. Where a given octet must take on a specific value, the
syntax X'hh' is used to denote the value of the single octet in that
field. When the word 'Variable' is used, it indicates that the
corresponding field has a variable length defined either by an
associated (one or two octet) length field, or by a data type field.
2. Procedure
A client MUST send its datagrams to the UDP relay server at
the UDP port indicated by BND.PORT in the reply to the UDP ASSOCIATE
request. If the selected authentication method provides
encapsulation for the purposes of authenticity, integrity, and/or
confidentiality, the datagram MUST be encapsulated using the
appropriate encapsulation. Each UDP datagram carries a UDP request
header with it:
+-----+-------+-------+--------------+
| VER | TOKEN | CMD | PARAMETERS |
+-----+-------+-------+--------------+
| 1 | 8 | 1 | 0 to 255 |
+-----+-------+-------+--------------+
The VER field is set to X'01' for this version of the protocol. The
TOKEN field contains 8 bytes that authenticates the request. The
CMD field describes the command that will be run. The
PARAMETERS field describes the parameters that will be passed
depending on the CMD field value.
The values currently defined for CMD are:
o X'00' GET METRICS
o X'01' GET BUFFER SIZE
o X'02' SET BUFFER SIZE
o X'03' GET TIMEOUT VALUE
o X'04' SET TIMEOUT VALUE
o X'05' GET USER PAGES
o X'06' LIST USERS
o X'07' GET USER LAST CONNECTION
o X'08' MODIFY USERNAME
o X'09' MODIFY PASSWORD
o X'0A' ADD USER
o X'0B' DELETE USER
o X'0C' GET PASSWORD DISSECTOR STATUS
o X'0D' ENABLE/DISABLE PASSWORD DISSECTOR
o X'0E' GET PROXY AUTHENTICATION STATUS
o X'0F' ENABLE/DISABLE PROXY AUTHENTICATION
o X'10' GET PROXY SERVER STATUS
o X'11' START/STOP PROXY SERVER
o X'12' RESET PROXY SERVER
When a UDP relay server decides to relay a UDP datagram, it does so
silently, without any notification to the requesting client.
Similarly, it will drop datagrams it cannot or will not relay. When
a UDP relay server receives a reply datagram from a remote host, it
MUST encapsulate that datagram using the above UDP request header,
and any authentication-method-dependent encapsulation.
The FRAG field indicates whether this datagram is one of a
number of fragments or not. If implemented, the high-order bit indicates
end-of-fragment sequence, while a value of X'00' indicates that this
datagram is standalone. Values between 1 and 127 indicate the
fragment position within a fragment sequence. Each receiver will
have a REASSEMBLY QUEUE and a REASSEMBLY TIMER associated with these
fragments. The reassembly queue must be reinitialized and the
associated fragments abandoned whenever the REASSEMBLY TIMER expires,
or a new datagram arrives carrying a FRAG field whose value is less
than the highest FRAG value processed for this fragment sequence.
The reassembly timer MUST be no less than 5 seconds. It is
recommended that fragmentation be avoided by applications wherever
possible.
Implementation of fragmentation is optional; an implementation that
does not support fragmentation MUST drop any datagram whose FRAG
field is other than X'00'.
3. Requests
Once the method-dependent subnegotiation has completed, the client
sends the request details. If the negotiated method includes
encapsulation for purposes of integrity checking and/or
confidentiality, these requests MUST be encapsulated in the method -
dependent encapsulation.
The SOCKS request is formed as follows:
+----+-----+-------+------+----------+----------+
|VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | X'00' | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
Where:
o VER protocol version: X'05'
o CMD
o CONNECT X'01'
o BIND X'02'
o UDP ASSOCIATE X'03'
o RSV RESERVED
o ATYP address type of following address
o IP V4 address: X'01'
o DOMAINNAME: X'03'
o IP V6 address: X'04'
o DST.ADDR desired destination address
o DST.PORT desired destination port in network octet
order
The SOCKS server will typically evaluate the request based on source
and destination addresses, and return one or more reply messages, as
appropriate for the request type.
5. Addressing
In an address field (DST.ADDR, BND.ADDR), the ATYP field specifies
the type of address contained within the field:
o X'01'
the address is a version-4 IP address, with a length of 4 octets
o X'03'
the address field contains a fully-qualified domain name. The first
octet of the address field contains the number of octets of name that
follow, there is no terminating NUL octet.
o X'04'
the address is a version-6 IP address, with a length of 16 octets.
6. Replies
The SOCKS request information is sent by the client as soon as it has
established a connection to the SOCKS server, and completed the
authentication negotiations. The server evaluates the request, and
returns a reply formed as follows:
+----+-----+-------+------+----------+----------+
|VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | X'00' | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
Where:
o VER protocol version: X'05'
o REP Reply field:
o X'00' succeeded
o X'01' general SOCKS server failure
o X'02' connection not allowed by ruleset
o X'03' Network unreachable
o X'04' Host unreachable
o X'05' Connection refused
o X'06' TTL expired
o X'07' Command not supported
o X'08' Address type not supported
o X'09' to X'FF' unassigned
o RSV RESERVED
o ATYP address type of following address
o IP V4 address: X'01'
o DOMAINNAME: X'03'
o IP V6 address: X'04'
o BND.ADDR server bound address
o BND.PORT server bound port in network octet order
Fields marked RESERVED (RSV) must be set to X'00'.
If the chosen method includes encapsulation for purposes of
authentication, integrity and/or confidentiality, the replies are
encapsulated in the method-dependent encapsulation.
CONNECT
In the reply to a CONNECT, BND.PORT contains the port number that the
server assigned to connect to the target host, while BND.ADDR
contains the associated IP address. The supplied BND.ADDR is often
different from the IP address that the client uses to reach the SOCKS
server, since such servers are often multi-homed. It is expected
that the SOCKS server will use DST.ADDR and DST.PORT, and the
client-side source address and port in evaluating the CONNECT
request.
BIND
The BIND request is used in protocols which require the client to
accept connections from the server. FTP is a well-known example,
which uses the primary client-to-server connection for commands and
status reports, but may use a server-to-client connection for
transferring data on demand (e.g. LS, GET, PUT).
It is expected that the client side of an application protocol will
use the BIND request only to establish secondary connections after a
primary connection is established using CONNECT. In is expected that
a SOCKS server will use DST.ADDR and DST.PORT in evaluating the BIND
request.
Two replies are sent from the SOCKS server to the client during a
BIND operation. The first is sent after the server creates and binds
a new socket. The BND.PORT field contains the port number that the
SOCKS server assigned to listen for an incoming connection. The
BND.ADDR field contains the associated IP address. The client will
typically use these pieces of information to notify (via the primary
or control connection) the application server of the rendezvous
address. The second reply occurs only after the anticipated incoming
connection succeeds or fails.
In the second reply, the BND.PORT and BND.ADDR fields contain the
address and port number of the connecting host.
UDP ASSOCIATE
The UDP ASSOCIATE request is used to establish an association within
the UDP relay process to handle UDP datagrams. The DST.ADDR and
DST.PORT fields contain the address and port that the client expects
to use to send UDP datagrams on for the association. The server MAY
use this information to limit access to the association. If the
client is not in possesion of the information at the time of the UDP
ASSOCIATE, the client MUST use a port number and address of all
zeros.
A UDP association terminates when the TCP connection that the UDP
ASSOCIATE request arrived on terminates.
In the reply to a UDP ASSOCIATE request, the BND.PORT and BND.ADDR
fields indicate the port number/address where the client MUST send
UDP request messages to be relayed.
Reply Processing
When a reply (REP value other than X'00') indicates a failure, the
SOCKS server MUST terminate the TCP connection shortly after sending
the reply. This must be no more than 10 seconds after detecting the
condition that caused a failure.
If the reply code (REP value of X'00') indicates a success, and the
request was either a BIND or a CONNECT, the client may now start
passing data. If the selected authentication method supports
encapsulation for the purposes of integrity, authentication and/or
confidentiality, the data are encapsulated using the method-dependent
encapsulation. Similarly, when data arrives at the SOCKS server for
the client, the server MUST encapsulate the data as appropriate for
the authentication method in use.
8. Security Considerations
This document describes a protocol for the application-layer
traversal of IP network firewalls. The security of such traversal is
highly dependent on the particular authentication and encapsulation
methods provided in a particular implementation, and selected during
negotiation between SOCKS client and SOCKS server.
Careful consideration should be given by the administrator to the
selection of authentication methods.
9. References
[1] Koblas, D., "SOCKS", Proceedings: 1992 Usenix Security Symposium.

View File

@ -2,8 +2,9 @@
#define ARGS_H
#include <stdbool.h>
#include <stdint.h>
#define MAX_USERS 500
#define MAX_USERS 10
struct users {
char * name;
@ -18,10 +19,12 @@ struct socks5args {
bool disectors_enabled;
int nusers;
struct users users[MAX_USERS];
};
void parse_args(const int argc, char ** argv, struct socks5args * args);
void parse_args(int argc, char ** argv, struct socks5args * args);
#endif

45
include/auth.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef AUTH_H
#define AUTH_H
#include <stdbool.h>
#include <stdint.h>
#include "buffer.h"
enum auth_state {
auth_version,
auth_ulen,
auth_uname,
auth_plen,
auth_passwd,
auth_done,
auth_error_unsupported_version,
};
struct auth_parser {
void (* on_username) (struct auth_parser * parser, char * username);
void (* on_password) (struct auth_parser * parser, char * password);
void * data;
enum auth_state state;
int user_pos;
char username[255];
char idx;
char password[255];
uint8_t * verified;
uint8_t remaining;
};
void auth_parser_init(struct auth_parser *p);
enum auth_state auth_parser_feed(struct auth_parser * p, uint8_t b);
enum auth_state auth_consume(buffer * b, struct auth_parser * p, bool * errored);
bool auth_is_done(enum auth_state state, bool * errored);
extern const char * auth_error(const struct auth_parser *p);
void auth_parser_close(struct auth_parser *p);
extern int auth_marshall(buffer * b, uint8_t method);
#endif

93
include/cmd.h Normal file
View File

@ -0,0 +1,93 @@
#ifndef CMD_H
#define CMD_H
#include <stdbool.h>
#include <stdint.h>
#include "buffer.h"
#define CMD_GET_METRICS 0x00
#define CMD_GET_BUFFER_SIZE 0x01
#define CMD_SET_BUFFER_SIZE 0x02
#define CMD_GET_TIMEOUT 0x03
#define CMD_SET_TIMEOUT 0x04
#define CMD_GET_USER_PAGES 0x05
#define CMD_LIST_USERS 0x06
#define CMD_GET_USER_LAST_CONNECTION 0x07
#define CMD_MODIFY_USERNAME 0x08
#define CMD_MODIFY_PASSWORD 0x09
#define CMD_ADD_USER 0x0A
#define CMD_DELETE_USER 0x00B
#define CMD_GET_PASSWORD_DISSECTOR_STATUS 0x0C
#define CMD_MODIFY_PASSWORD_DISSECTOR_STATUS 0x0D
#define CMD_GET_AUTHENTICATION_STATUS 0x0E
#define CMD_MODIFY_AUTHENTICATION_STATUS 0x0F
#define CMD_GET_PROXY_SERVER_STATUS 0x10
#define CMD_CHANGE_PROXY_SERVER_STATUS 0x11
#define CMD_RESTART_PROXY_SERVER 0x12
#define CMD_NOT_SUPPORTED_CMD 0xFF
enum cmd_state {
cmd_version,
cmd_token,
cmd_cmd,
cmd_parameters,
cmd_done,
cmd_error_unsupported_version,
cmd_error_invalid_token,
cmd_error_unsupported_command,
};
enum param_state {
param_ulen,
param_uname,
param_new_ulen,
param_new_uname,
param_plen,
param_passwd,
param_byte,
param_value,
};
enum cmd_response_status {
status_succeeded = 0x00,
status_failure = 0x01,
};
struct cmd_parser {
void (* on_token) (struct cmd_parser * parser, const uint64_t token);
void (* on_cmd) (struct cmd_parser * parser, const uint8_t cmd);
void * data;
enum cmd_state state;
enum param_state param_state;
uint8_t * cmd;
uint8_t remaining;
uint8_t * verified;
uint64_t token;
uint8_t idx;
char username[255];
char new_username[255];
char password[255];
uint8_t byte;
uint16_t value;
void * parameters;
};
void cmd_parser_init(struct cmd_parser * p);
enum cmd_state cmd_parser_feed(struct cmd_parser * p, uint8_t b);
enum cmd_state cmd_consume(buffer * b, struct cmd_parser * p, bool * errored);
bool cmd_is_done(enum cmd_state state, bool * errored);
extern const char * cmd_error(const struct cmd_parser * p);
void cmd_parser_close(struct cmd_parser * p);
extern int cmd_marshall(buffer * b, uint8_t method);
#endif

43
include/confignio.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef CONFIGNIO_H
#define CONFIGNIO_H
#include "stm.h"
#include "cmd.h"
#define BUFF_SIZE 4096
struct cmd_st {
/** buffer utilizado para I/O */
buffer *rb, *wb;
struct cmd_parser parser;
/** el método de autenticación seleccionado */
uint8_t cmd;
/** está verificado */
uint8_t verified;
uint8_t status;
};
struct config {
struct sockaddr_storage client_addr;
socklen_t client_addr_len;
/** maquinas de estados */
struct state_machine stm;
union {
struct cmd_st cmd;
} client;
// TODO: decidir tamaño del buffer
uint8_t raw_buff_a[BUFF_SIZE], raw_buff_b[BUFF_SIZE];
buffer read_buffer, write_buffer;
unsigned references;
struct config * next;
};
void config_read(struct selector_key *key);
void config_write(struct selector_key *key);
#endif

View File

@ -5,7 +5,8 @@
#include <stdint.h>
#include "buffer.h"
static const uint8_t METHOD_NO_AUTHENTICATION_REQURED = 0x00;
static const uint8_t METHOD_NO_AUTHENTICATION_REQUIRED = 0x00;
static const uint8_t METHOD_USERNAME_PASSWORD_AUTHENTICATION = 0x02;
static const uint8_t METHOD_NO_ACCEPTABLE_METHODS = 0xFF;
enum hello_state {
@ -21,6 +22,7 @@ struct hello_parser {
void * data;
enum hello_state state;
uint8_t remaining;
uint8_t has_selected;
};
void hello_parser_init(struct hello_parser *p);
@ -29,12 +31,12 @@ enum hello_state hello_parser_feed(struct hello_parser * p, uint8_t b);
enum hello_state hello_consume(buffer * b, struct hello_parser * p, bool * errored);
bool hello_is_done(const enum hello_state state, bool * errored);
bool hello_is_done(enum hello_state state, bool * errored);
extern const char * hello_error(const struct hello_parser *p);
void hello_parser_close(struct hello_parser *p);
extern int hello_marshall(buffer * b, const uint8_t method);
extern int hello_marshall(buffer * b, uint8_t method);
#endif

View File

@ -1,4 +1,8 @@
#ifndef SERVER_H
#define SERVER_H
int findUser(char * username);
int checkPassword(int idx, char * password);
#endif

View File

@ -92,7 +92,7 @@ void parse_args(const int argc, char **argv, struct socks5args *args) {
break;
case 'u':
if (nusers >= MAX_USERS) {
fprintf(stderr, "Maximun number of command line users reached: %d.\n", MAX_USERS);
fprintf(stderr, "Maximum number of command line users reached: %d.\n", MAX_USERS);
exit(1);
} else {
user(optarg, args->users + nusers);
@ -102,14 +102,14 @@ void parse_args(const int argc, char **argv, struct socks5args *args) {
case 'v':
version();
exit(0);
break;
default:
fprintf(stderr, "unknown argument %d.\n", c);
exit(1);
}
}
args->nusers = nusers;
if (optind < argc) {
fprintf(stderr, "Argument not accepted: ");
while (optind < argc) {

138
src/auth.c Normal file
View File

@ -0,0 +1,138 @@
// This is a personal academic project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <stdio.h>
#include <stdlib.h>
#include "auth.h"
void auth_parser_init(struct auth_parser *p) {
p->state = auth_version;
p->remaining = 0;
}
enum auth_state auth_parser_feed(struct auth_parser * p, uint8_t b) {
switch (p->state) {
case auth_version:
if (0x01 == b) {
p->state = auth_ulen;
} else {
p->state = auth_error_unsupported_version;
}
break;
case auth_ulen:
p->remaining = b;
p->state = auth_uname;
if (p->remaining == 0) {
p->state = auth_plen;
}
break;
case auth_uname:
*(p->username + p->idx++) = (char) b;
p->remaining--;
if (p->remaining == 0) {
*(p->username + p->idx++) = 0;
if (NULL != p->on_username) {
p->on_username(p, p->username);
}
p->idx = 0;
p->state = auth_plen;
}
break;
case auth_plen:
p->remaining = b;
p->state = auth_passwd;
if (p->remaining == 0) {
p->state = auth_done;
}
break;
case auth_passwd:
*(p->password + p->idx++) = (char) b;
p->remaining--;
if (p->remaining == 0) {
*(p->password + p->idx++) = 0;
if (NULL != p->on_password) {
p->on_password(p, p->password);
}
p->idx = 0;
p->state = auth_done;
}
break;
case auth_done:
case auth_error_unsupported_version:
break;
default:
fprintf(stderr, "unknown state %d\n", p->state);
abort();
}
return p->state;
}
extern bool auth_is_done(const enum auth_state state, bool * errored) {
bool ret = true;
switch (state) {
case auth_error_unsupported_version:
if (0 != errored) {
*errored = true;
}
break;
case auth_done:
break;
default:
ret = false;
break;
}
return ret;
}
extern const char * auth_error(const struct auth_parser *p) {
char * ret;
switch (p->state) {
case auth_error_unsupported_version:
ret = "unsupported version";
break;
default:
ret = "";
break;
}
return ret;
}
extern void auth_parser_close(struct auth_parser * p) {}
extern enum auth_state auth_consume(buffer * b, struct auth_parser *p, bool * errored) {
enum auth_state st = p->state;
while (buffer_can_read(b)) {
const uint8_t c = buffer_read(b);
st = auth_parser_feed(p, c);
if (auth_is_done(st, errored)) {
break;
}
}
return st;
}
extern int auth_marshall(buffer * b, const uint8_t status) {
size_t n;
uint8_t * buff = buffer_write_ptr(b, &n);
if (n < 2) {
return -1;
}
buff[0] = 0x01;
buff[1] = status;
buffer_write_adv(b, 2);
return 2;
}

View File

@ -1,7 +1,270 @@
// This is a personal academic project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#include <getopt.h>
int main(int argc, char *argv[]) {
#define MAX_SIZE 524
#define MAX_RECV_SIZE 1024
#define N(x) (sizeof(x)/sizeof((x)[0]))
static void version(void) {
fprintf(stderr, "BProxy v1.0\nITBA Protocolos de Comunicación 2021/1 -- Grupo 7\nVer LICENSE.md\n");
}
static void usage(const char *progname) {
fprintf(stderr,
"Usage: %s [token] [OPTION]\n\n"
" -h Imprime la ayuda y termina.\n"
" -m Obtener métricas del servidor.\n"
" -b Obtener el tamaño del buffer.\n"
" -B <value> Modificar el tamaño del buffer.\n"
" -t Obtener el valor del timeout.\n"
" -T <value> Modificar el valor del timeout.\n"
" -f Obtener cantidad de páginas de nombres de usuario.\n"
" -u <page> Obtener nombres de usuario de la página especificada.\n"
" -l <name> Obtener la última conexión del usuario específicado.\n"
" -c <name>:<new_name> Nombre de usuario y nuevo nombre de usuario\n"
" -p <name>:<new_pass> Nombre de usuario y su nueva contraseña\n"
" -n <name>:<pass> Nombre de usuario y contraseña del usuario que desea ser agregado\n"
" -d <name> Nombre del usuario que desea ser borrado\n"
" -s Obtener el estado del disector de contraseñas POP3.\n"
" -S <estado> Modificar el estado del disector de contraseñas POP3.\n"
" -a Obtener el estado de la autorización del proxy.\n"
" -A <estado> Modificar el estado de la autorización del proxy.\n"
" -z Obtener el estado del servidor proxy.\n"
" -Z <estado> Modificar el estado del servidor proxy.\n"
" -r Reinicia el servidor\n\n",
progname);
exit(1);
}
void parse_args(int argc, char ** argv, uint8_t * buffer) {
int c;
if (argc <= 1 || argv[1][0] == '-') {
fprintf(stderr, "You must enter a valid token.\n");
exit(EXIT_FAILURE);
}
uint64_t token = atol(argv[1]);
buffer[0] = 0x01;
buffer[1] = token>>56;
buffer[2] = (uint8_t) (token>>48);
buffer[3] = (uint8_t) (token>>40);
buffer[4] = (uint8_t) (token>>32);
buffer[5] = (uint8_t) (token>>24);
buffer[6] = (uint8_t) (token>>16);
buffer[7] = (uint8_t) (token>>8);
buffer[8] = (uint8_t) token;
for(int i=1; i<argc; ++i)
argv[i] = argv[i+1];
argc--;
c = getopt(argc, argv, "hmbtfsazrB:T:l:u:c:p:n:d:S:A:Z:");
// retorna : o ? si le falta el argumento
if (c == -1)
return;
if (optind > 3 || argc == 2) {
fprintf(stderr, "Only one option allowed\n");
exit(EXIT_FAILURE);
}
uint16_t value;
uint8_t ulen, plen, nulen;
char * p;
switch (c) {
case 'h':
usage(argv[0]);
break;
case 'm':
buffer[9] = 0x00;
break;
case 'b':
buffer[9] = 0x01;
break;
case 'B':
buffer[9] = 0x02;
buffer[9] = (uint16_t) atoi(optarg);
break;
case 't':
buffer[9] = 0x03;
break;
case 'T':
buffer[9] = 0x04;
value = atoi(optarg);
buffer[10] = value >> 8;
buffer[11] = (uint8_t) value;
break;
case 'f':
buffer[9] = 0x05;
break;
case 'u':
buffer[9] = 0x06;
value = atoi(optarg);
buffer[10] = value >> 8;
buffer[11] = (uint8_t) value;
break;
case 'l':
buffer[9] = 0x07;
ulen = strlen(optarg);
buffer[10] = ulen;
strcat((char *) buffer, optarg);
break;
case 'c':
buffer[9] = 0x08;
char *n = strchr(optarg, ':');
*n = 0;
n++;
ulen = strlen(optarg);
nulen = strlen(n);
buffer[10] = ulen;
strcat((char *) buffer, optarg);
buffer[11+ulen] = nulen;
strcat((char *) buffer, n);
break;
case 'p':
buffer[9] = 0x09;
p = strchr(optarg, ':');
*p = 0;
p++;
ulen = strlen(optarg);
plen = strlen(p);
buffer[10] = ulen;
strcat((char *) buffer, optarg);
buffer[11+ulen] = plen;
strcat((char *) buffer, p);
break;
case 'n':
buffer[9] = 0x0A;
p = strchr(optarg, ':');
*p = 0;
p++;
ulen = strlen(optarg);
plen = strlen(p);
buffer[10] = ulen;
strcat((char *) buffer, optarg);
buffer[11+ulen] = plen;
strcat((char *) buffer, p);
break;
case 'd':
buffer[9] = 0x0B;
ulen = strlen(optarg);
buffer[10] = ulen;
strcat((char *) buffer, optarg);
break;
case 's':
buffer[9] = 0x0C;
break;
case 'S':
buffer[9] = 0x0D;
buffer[10] = (uint8_t) atoi(optarg);
break;
case 'a':
buffer[9] = 0x0E;
break;
case 'A':
buffer[9] = 0x0F;
buffer[10] = (uint8_t) atoi(optarg);
break;
case 'z':
buffer[9] = 0x10;
break;
case 'Z':
buffer[9] = 0x11;
buffer[10] = (uint8_t) atoi(optarg);
break;
case 'r':
buffer[9] = 0x12;
break;
case 'v':
version();
exit(EXIT_SUCCESS);
default:
fprintf(stderr, "Unknown argument %d.\n", c);
exit(EXIT_FAILURE);
}
// if (optind < argc + 1) {
// fprintf(stderr, "Argument not accepted: ");
// while (optind < argc) {
// fprintf(stderr, "%s ", argv[optind++]);
// }
// fprintf(stderr, "\n");
// exit(1);
// }
}
int main(int argc, char **argv) {
const char *err_msg = NULL;
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(8080);
const int client = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (client < 0) {
err_msg = "Unable to create UDP socket";
goto finally;
}
if (connect(client, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
err_msg = "Unable to connect to UDP socket";
goto finally;
}
uint8_t buffer[MAX_SIZE] = {0};
parse_args(argc, argv, buffer);
for (int i = 0; i < 100; i++) {
fprintf(stderr, "%x ", buffer[i]);
}
fprintf(stderr, "\n");
ssize_t sent = sendto(client, buffer, N(buffer), 0, NULL, sizeof(addr));
if (sent < 0) {
err_msg = "Unable to send cmd";
goto finally;
}
char * recv_buffer = malloc(MAX_RECV_SIZE);
ssize_t received = recvfrom(client, recv_buffer, sizeof(recv_buffer), 0, NULL, NULL);
if (received < 0) {
err_msg = "Unable to receive cmd";
goto finally;
}
printf("%s\n", recv_buffer);
err_msg = NULL;
int ret = EXIT_SUCCESS;
finally:
free(recv_buffer);
if (err_msg) {
perror(err_msg);
ret = EXIT_FAILURE;
}
if (client >= 0) {
close(client);
}
return ret;
}

185
src/cmd.c Normal file
View File

@ -0,0 +1,185 @@
// This is a personal academic project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "cmd.h"
void cmd_parser_init(struct cmd_parser *p) {
p->state = cmd_version;
p->remaining = 0;
}
enum cmd_state cmd_parser_feed(struct cmd_parser * p, uint8_t b) {
switch (p->state) {
case cmd_version:
if (0x01 == b) {
p->state = cmd_token;
} else {
p->state = cmd_error_unsupported_version;
}
p->remaining = 8;
break;
case cmd_token:
p->remaining--;
p->token <<= 8;
p->token += b;
if (p->remaining == 0) {
p->state = cmd_cmd;
p->on_token(p, p->token);
fprintf(stderr, "test token hay magia acá\n");
fprintf(stderr, "token: %lx\n", p->token);
fprintf(stderr, "%d : %d\n", p->state, cmd_cmd);
}
break;
case cmd_cmd:
if (NULL != p->on_cmd) {
p->on_cmd(p, b); // cambia el state, param_state y remaining
}
break;
case cmd_parameters:
switch (p->param_state) {
case param_ulen:
p->remaining = b;
fprintf(stderr, "ulen: %d\n", p->remaining);
p->param_state = param_uname;
break;
case param_uname:
*(p->username + p->idx++) = (char) b;
p->remaining--;
if (p->remaining == 0) {
*(p->username + p->idx++) = 0;
fprintf(stderr, "old_uname: %s\n", p->username);
p->idx = 0;
if (*p->cmd == CMD_DELETE_USER || *p->cmd == CMD_GET_USER_LAST_CONNECTION)
p->state = cmd_done;
else if (*p->cmd == CMD_MODIFY_USERNAME)
p->param_state = param_new_ulen;
else if (*p->cmd == CMD_MODIFY_PASSWORD || *p->cmd == CMD_ADD_USER)
p->param_state = param_plen;
}
break;
case param_plen:
p->remaining = b;
p->param_state = param_passwd;
break;
case param_passwd:
*(p->password + p->idx++) = (char) b;
p->remaining--;
if (p->remaining == 0) {
*(p->password + p->idx++) = 0;
p->idx = 0;
p->state = cmd_done;
}
break;
case param_byte:
p->byte = b;
p->state = cmd_done;
break;
case param_new_ulen:
p->remaining = b;
p->param_state = param_new_uname;
break;
case param_new_uname:
*(p->new_username + p->idx++) = (char) b;
p->remaining--;
if (p->remaining == 0) {
*(p->new_username + p->idx++) = 0;
fprintf(stderr, "new_uname: %s", p->new_username);
p->idx = 0;
p->state = cmd_done;
}
break;
case param_value:
p->token += (uint16_t) (b * pow(255, --p->remaining));
if (p->remaining == 0) {
p->state = cmd_done;
}
}
break;
case cmd_done:
case cmd_error_unsupported_version:
case cmd_error_unsupported_command:
case cmd_error_invalid_token:
break;
default:
fprintf(stderr, "unknown state %d\n", p->state);
abort();
}
return p->state;
}
extern bool cmd_is_done(const enum cmd_state state, bool * errored) {
bool ret = true;
switch (state) {
case cmd_error_unsupported_version:
if (0 != errored) {
*errored = true;
}
break;
case cmd_done:
break;
default:
ret = false;
break;
}
return ret;
}
extern const char * cmd_error(const struct cmd_parser * p) {
char * ret;
switch (p->state) {
case cmd_error_unsupported_version:
ret = "Unsupported version";
break;
case cmd_error_unsupported_command:
ret = "Unsupported command";
break;
case cmd_error_invalid_token:
ret = "Invalid token";
break;
default:
ret = "";
break;
}
return ret;
}
extern void cmd_parser_close(struct cmd_parser * p) {}
extern enum cmd_state cmd_consume(buffer * b, struct cmd_parser * p, bool * errored) {
enum cmd_state st = p->state;
while (buffer_can_read(b)) {
const uint8_t c = buffer_read(b);
st = cmd_parser_feed(p, c);
if (cmd_is_done(st, errored)) {
break;
}
}
return st;
}
extern int cmd_marshall(buffer * b, const uint8_t status) {
size_t n;
uint8_t * buff = buffer_write_ptr(b, &n);
if (n < 2) {
return -1;
}
buff[0] = 0x01;
buff[1] = status;
buffer_write_adv(b, 2);
return 2;
}

206
src/confignio.c Normal file
View File

@ -0,0 +1,206 @@
// This is a personal academic project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include "buffer.h"
#include "selector.h"
#include "confignio.h"
#include "cmd.h"
#define N(x) (sizeof(x)/sizeof((x)[0]))
#define TOKEN 0x0FF1CEDEADB00B1E
//enum config_state {
// READ,
// WRITE,
// DONE,
// ERROR,
//};
static void on_token(struct cmd_parser * p, const uint64_t token) {
*p->verified = (token - (uint64_t) TOKEN == 0);
p->state = cmd_cmd;
if (!*p->verified)
p->state = cmd_error_invalid_token;
}
static void on_cmd(struct cmd_parser * p, const uint8_t cmd) {
uint8_t * selected = p->cmd;
switch (cmd) {
case CMD_GET_METRICS:
* selected = CMD_GET_METRICS;
break;
case CMD_GET_BUFFER_SIZE:
* selected = CMD_GET_BUFFER_SIZE;
break;
case CMD_SET_BUFFER_SIZE:
* selected = CMD_SET_BUFFER_SIZE;
p->state = cmd_parameters;
p->remaining = 2;
p->param_state = param_value;
break;
case CMD_GET_TIMEOUT:
* selected = CMD_GET_TIMEOUT;
break;
case CMD_SET_TIMEOUT:
* selected = CMD_SET_TIMEOUT;
p->state = cmd_parameters;
p->remaining = 2;
p->param_state = param_value;
break;
case CMD_GET_USER_PAGES:
* selected = CMD_GET_USER_PAGES;
break;
case CMD_LIST_USERS:
* selected = CMD_LIST_USERS;
p->state = cmd_parameters;
p->param_state = param_byte;
p->remaining = 1;
break;
case CMD_GET_USER_LAST_CONNECTION:
* selected = CMD_GET_USER_LAST_CONNECTION;
p->state = cmd_parameters;
p->param_state = param_ulen;
p->remaining = 1;
break;
case CMD_MODIFY_USERNAME:
* selected = CMD_MODIFY_USERNAME;
p->state = cmd_parameters;
p->param_state = param_ulen;
p->remaining = 1;
break;
case CMD_MODIFY_PASSWORD:
* selected = CMD_MODIFY_PASSWORD;
p->state = cmd_parameters;
p->param_state = param_ulen;
p->remaining = 1;
break;
case CMD_ADD_USER:
* selected = CMD_ADD_USER;
p->state = cmd_parameters;
p->param_state = param_ulen;
p->remaining = 1;
break;
case CMD_DELETE_USER:
* selected = CMD_DELETE_USER ;
p->state = cmd_parameters;
p->param_state = param_ulen;
p->remaining = 1;
break;
case CMD_GET_PASSWORD_DISSECTOR_STATUS:
* selected = CMD_GET_PASSWORD_DISSECTOR_STATUS;
break;
case CMD_MODIFY_PASSWORD_DISSECTOR_STATUS:
* selected = CMD_MODIFY_PASSWORD_DISSECTOR_STATUS;
* selected = CMD_LIST_USERS;
p->state = cmd_parameters;
p->param_state = param_byte;
p->remaining = 1;
break;
case CMD_GET_AUTHENTICATION_STATUS:
* selected = CMD_GET_AUTHENTICATION_STATUS;
break;
case CMD_MODIFY_AUTHENTICATION_STATUS:
* selected = CMD_MODIFY_AUTHENTICATION_STATUS;
* selected = CMD_LIST_USERS;
p->state = cmd_parameters;
p->param_state = param_byte;
p->remaining = 1;
break;
case CMD_GET_PROXY_SERVER_STATUS:
* selected = CMD_GET_PROXY_SERVER_STATUS;
break;
case CMD_CHANGE_PROXY_SERVER_STATUS:
* selected = CMD_CHANGE_PROXY_SERVER_STATUS;
* selected = CMD_LIST_USERS;
p->state = cmd_parameters;
p->param_state = param_byte;
p->remaining = 1;
break;
case CMD_RESTART_PROXY_SERVER:
* selected = CMD_RESTART_PROXY_SERVER;
break;
default:
* selected = CMD_NOT_SUPPORTED_CMD;
}
}
void cmd_process(struct cmd_st * d) {
// unsigned ret = WRITE;
uint8_t m = d->status;
const uint8_t r = m ? 0x00 : 0xFF;
if (cmd_marshall(d->wb, (enum cmd_response_status) r) == -1) {
// ret = ERROR;
}
// return ret;
}
#define ATTACHMENT(key) ( (struct config *)(key)->data)
static void config_read_init(struct selector_key * key) {
struct cmd_st *d = &ATTACHMENT(key)->client.cmd;
d->rb = &(ATTACHMENT(key)->read_buffer);
d->wb = &(ATTACHMENT(key)->write_buffer);
d->parser.cmd = &d->cmd;
d->parser.verified = &d->verified;
d->parser.on_cmd = on_cmd;
d->parser.on_token = on_token;
d->parser.idx = 0;
cmd_parser_init(&d->parser);
}
void config_read(struct selector_key *key) {
struct cmd_st *d = &ATTACHMENT(key)->client.cmd;
bool error = false;
uint8_t *ptr;
size_t count;
ssize_t n;
struct sockaddr_storage client_addr;
socklen_t client_addr_len = sizeof(client_addr);
config_read_init(key);
ptr = buffer_write_ptr(d->rb, &count);
n = recvfrom(key->fd, ptr, BUFF_SIZE, 0, (struct sockaddr *) &client_addr, &client_addr_len);
if (n > 0) {
buffer_write_adv(d->rb, n);
const enum cmd_state st = cmd_consume(d->rb, &d->parser, &error);
if (cmd_is_done(st, 0)) {
if (SELECTOR_SUCCESS == selector_set_interest_key(key, OP_WRITE)) {
cmd_process(d);
} else {
return;
}
}
} else {
return;
}
}
void config_write(struct selector_key *key) {
struct cmd_st *d = &ATTACHMENT(key)->client.cmd;
uint8_t *ptr;
size_t count;
ssize_t n;
struct sockaddr_storage client_addr;
socklen_t client_addr_len = sizeof(client_addr);
ptr = buffer_read_ptr(d->wb, &count);
n = sendto(key->fd, ptr, count, 0, (struct sockaddr *) &client_addr, sizeof(client_addr_len));
if (n == -1) {
return;
} else {
buffer_read_adv(d->wb, n);
if (!buffer_can_read(d->wb)) {
if (SELECTOR_SUCCESS != selector_set_interest_key(key, OP_READ)) {
return;
}
}
}
}

View File

@ -313,7 +313,7 @@ selector_destroy(fd_selector s) {
}
}
pthread_mutex_destroy(&s->resolution_mutex);
for(struct blocking_job *next, *j = s->resolution_jobs; j != NULL; j = j->next) {
for (struct blocking_job *next, *j = s->resolution_jobs; j != NULL;) {
next = j->next;
free(j);
j = next;
@ -479,9 +479,7 @@ handle_block_notifications(fd_selector s) {
.s = s,
};
pthread_mutex_lock(&s->resolution_mutex);
for(struct blocking_job *next, *j = s->resolution_jobs;
j != NULL ;
j = j->next) {
for (struct blocking_job *next, *j = s->resolution_jobs; j != NULL;) {
struct item *item = s->fds + j->fd;
if (ITEM_USED(item)) {

View File

@ -12,10 +12,16 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include "args.h"
#include "selector.h"
#include "socks5nio.h"
#include "confignio.h"
#include "buffer.h"
#define N(x) (sizeof(x)/sizeof((x)[0]))
static bool done = false;
@ -24,12 +30,11 @@ static void sigterm_handler(const int signal) {
done = true;
}
int main(int argc, char **argv) {
unsigned port = 1080;
static struct socks5args * args;
struct socks5args * args = malloc(sizeof(struct socks5args));
int main(int argc, char **argv) {
args = malloc(sizeof(struct socks5args));
parse_args(argc, argv, args);
free(args);
close(STDIN_FILENO);
@ -37,24 +42,33 @@ int main(int argc, char **argv) {
selector_status ss = SELECTOR_SUCCESS;
fd_selector selector = NULL;
// ------
// TCP
// ------
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
if (inet_pton(AF_INET, args->socks_addr, &addr.sin_addr) <= 0) {
err_msg = "Incorrect network address";
goto finally;
}
// addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(args->socks_port);
const int server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (server < 0) {
err_msg = "Unable to create socket";
err_msg = "Unable to create TCP socket";
goto finally;
}
fprintf(stdout, "Listening on TCP port %u\n", port);
fprintf(stdout, "Listening on TCP port %u\n", args->socks_port);
setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int));
if (bind(server, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
err_msg = "Unable to bind socket";
err_msg = "Unable to bind TCP socket";
goto finally;
}
@ -63,6 +77,46 @@ int main(int argc, char **argv) {
goto finally;
}
// ------
// UDP
// ------
struct sockaddr_in udp_addr;
memset(&udp_addr, 0, sizeof(udp_addr));
udp_addr.sin_family = AF_INET;
if (inet_pton(AF_INET, args->mng_addr, &udp_addr.sin_addr) <= 0) {
err_msg = "Incorrect network address";
goto finally;
}
// udp_addr.sin_addr.s_addr = htonl(INADDR_ANY);
udp_addr.sin_port = htons(args->mng_port);
const int udp_server = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (udp_server < 0) {
err_msg = "Unable to create UDP socket";
goto finally;
}
fprintf(stdout, "Listening on UDP port %u\n", args->mng_port);
setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int));
if (bind(udp_server, (struct sockaddr *) &udp_addr, sizeof(udp_addr)) < 0) {
err_msg = "Unable to bind UDP socket";
goto finally;
}
if (fcntl(udp_server, F_SETFL, O_NONBLOCK) < 0) {
err_msg = "Unable to put UDP socket into non-blocking mode";
goto finally;
}
// struct config * config_ret = calloc(1, sizeof(struct config));
struct config * config_ret = malloc(sizeof(struct config));
memset(config_ret, 0x00, sizeof(*config_ret));
buffer_init(&config_ret->read_buffer, N(config_ret->raw_buff_a), config_ret->raw_buff_a);
buffer_init(&config_ret->write_buffer, N(config_ret->raw_buff_b), config_ret->raw_buff_b);
signal(SIGTERM, sigterm_handler);
signal(SIGINT, sigterm_handler);
@ -98,6 +152,23 @@ int main(int argc, char **argv) {
err_msg = "Registering fd";
goto finally;
}
if (selector_fd_set_nio(udp_server) == -1) {
err_msg = "Getting config socket flags";
goto finally;
}
const struct fd_handler config = {
.handle_read = config_read,
.handle_write = config_write,
.handle_close = NULL,
};
ss = selector_register(selector, udp_server, &config, OP_READ, config_ret);
if (ss != SELECTOR_SUCCESS) {
err_msg = "Registering fd";
goto finally;
}
for(;!done;) {
err_msg = NULL;
ss = selector_select(selector);
@ -110,6 +181,9 @@ int main(int argc, char **argv) {
err_msg = "Closing";
}
free(args);
free(config_ret);
int ret = 0;
finally:
if (ss != SELECTOR_SUCCESS) {
@ -131,3 +205,15 @@ finally:
}
return ret;
}
int findUser(char * username) {
for (int i = 0; i < args->nusers; i++) {
if (!strcmp(args->users[i].name, username))
return i;
}
return -1;
}
int checkPassword(int idx, char * password) {
return !strcmp(args->users[idx].pass, password);
}

View File

@ -12,24 +12,28 @@
#include <arpa/inet.h>
#include "hello.h"
#include "auth.h"
#include "request.h"
#include "buffer.h"
#include "selector.h"
#include "server.h"
#include "stm.h"
#include "socks5nio.h"
#include "netutils.h"
#define N(x) (sizeof(x)/sizeof((x)[0]))
#define BUFF_SIZE 2048 // TODO: decidir tamaño del buffer
#define BUFF_SIZE 4096 // TODO: decidir tamaño del buffer (2048 muy lento para archivos grandes!!!)
// TODO: hacer que cambie con nuestro proto
int auth_active = 1;
/** maquina de estados general */
enum socks_v5state {
HELLO_READ,
HELLO_WRITE,
// AUTH_READ,
// AUTH_WRITE,
AUTH_READ,
AUTH_WRITE,
REQUEST_READ,
REQUEST_RESOLV,
@ -52,6 +56,15 @@ struct hello_st {
struct hello_parser parser;
/** el método de autenticación seleccionado */
uint8_t method;
/** verifico haber elegido */
char has_selected;
};
struct auth_st {
/** buffer utilizado para I/O */
buffer *rb, *wb;
struct auth_parser parser;
uint8_t verified;
};
struct request_st {
@ -114,6 +127,7 @@ struct socks5 {
struct hello_st hello;
struct request_st request;
struct copy copy;
struct auth_st auth;
} client;
/** estados para el origin_fd */
union {
@ -228,8 +242,7 @@ static const struct fd_handler socks5_handler = {
};
/** Intenta aceptar la nueva conexión entrante*/
void
socksv5_passive_accept(struct selector_key *key) {
void socksv5_passive_accept(struct selector_key *key) {
struct sockaddr_storage client_addr;
socklen_t client_addr_len = sizeof(client_addr);
struct socks5 *state = NULL;
@ -271,8 +284,20 @@ fail:
static void on_hello_method(struct hello_parser * p, const uint8_t method) {
uint8_t * selected = p->data;
if (METHOD_NO_AUTHENTICATION_REQURED == method) {
if (auth_active) {
if (METHOD_USERNAME_PASSWORD_AUTHENTICATION == method) {
*selected = method;
p->has_selected = 1;
}
}
else {
if (METHOD_NO_AUTHENTICATION_REQUIRED == method) {
*selected = method;
p->has_selected = 1;
}
}
if (!p->has_selected) {
*selected = METHOD_NO_ACCEPTABLE_METHODS;
}
}
@ -283,14 +308,15 @@ static void hello_read_init(const unsigned state, struct selector_key * key) {
d->rb = &(ATTACHMENT(key)->read_buffer);
d->wb = &(ATTACHMENT(key)->write_buffer);
d->parser.data = &d->method;
d->parser.on_authentication_method = on_hello_method, hello_parser_init(
&d->parser);
d->parser.on_authentication_method = on_hello_method;
d->parser.has_selected = 0;
hello_parser_init(&d->parser);
}
static unsigned
hello_process(const struct hello_st * d);
/** lee todos los bytes del mensaje de tipo `hello' y inicia su proceso */
/** lee todos los bytes del mensaje de tipo `hello' e inicia su proceso */
static unsigned hello_read(struct selector_key * key) {
struct hello_st *d = &ATTACHMENT(key)->client.hello;
unsigned ret = HELLO_READ;
@ -320,12 +346,11 @@ static unsigned hello_read(struct selector_key * key) {
}
/** procesamiento del mensaje `hello' */
static unsigned
hello_process(const struct hello_st* d) {
static unsigned hello_process(const struct hello_st * d) {
unsigned ret = HELLO_WRITE;
uint8_t m = d->method;
const uint8_t r = (m == METHOD_NO_ACCEPTABLE_METHODS) ? 0xFF : 0x00;
const uint8_t r = (m == METHOD_NO_ACCEPTABLE_METHODS) ? 0xFF : m;
if (hello_marshall(d->wb, (enum socks_response_status) r) == -1) {
ret = ERROR;
}
@ -349,6 +374,116 @@ static unsigned hello_write(struct selector_key * key) {
size_t count;
ssize_t n;
ptr = buffer_read_ptr(d->wb, &count);
n = send(key->fd, ptr, count, MSG_NOSIGNAL);
if (n == -1) {
ret = ERROR;
} else {
buffer_read_adv(d->wb, n);
if (!buffer_can_read(d->wb)) {
if (SELECTOR_SUCCESS == selector_set_interest_key(key, OP_READ)) {
if (d->method == METHOD_NO_AUTHENTICATION_REQUIRED)
ret = REQUEST_READ;
else if (d->method == METHOD_USERNAME_PASSWORD_AUTHENTICATION)
ret = AUTH_READ;
} else {
ret = ERROR;
}
}
}
return ret;
}
////////////////////////////////////////////////////////////////////////////////
// AUTH
////////////////////////////////////////////////////////////////////////////////
/** callback del parser utilizado en `read_auth' */
static void on_auth_username(struct auth_parser * p, char * username) {
int * idx = &p->user_pos;
*idx = findUser(username);
}
/** callback del parser utilizado en `read_auth' */
static void on_auth_password(struct auth_parser * p, char * password) {
uint8_t * verified = p->verified;
if (checkPassword(p->user_pos, password))
* verified = 1;
}
/** inicializa las variables de los estados HELLO_… */
static void auth_read_init(const unsigned state, struct selector_key * key) {
struct auth_st *d = &ATTACHMENT(key)->client.auth;
d->rb = &(ATTACHMENT(key)->read_buffer);
d->wb = &(ATTACHMENT(key)->write_buffer);
d->parser.verified = &d->verified;
d->parser.on_username = on_auth_username;
d->parser.on_password = on_auth_password;
*d->parser.verified = 0;
d->parser.idx = 0;
auth_parser_init(&d->parser);
}
/** procesamiento del mensaje `auth' */
static unsigned auth_process(const struct auth_st * d);
/** lee todos los bytes del mensaje de tipo `auth' e inicia su proceso */
static unsigned auth_read(struct selector_key * key) {
struct auth_st *d = &ATTACHMENT(key)->client.auth;
unsigned ret = AUTH_READ;
bool error = false;
uint8_t *ptr;
size_t count;
ssize_t n;
ptr = buffer_write_ptr(d->rb, &count);
n = recv(key->fd, ptr, count, 0);
if(n > 0) {
buffer_write_adv(d->rb, n);
const enum auth_state st = auth_consume(d->rb, &d->parser, &error);
if(auth_is_done(st, 0)) {
if(SELECTOR_SUCCESS == selector_set_interest_key(key, OP_WRITE)) {
ret = auth_process(d);
} else {
ret = ERROR;
}
}
} else {
ret = ERROR;
}
return error ? ERROR : ret;
}
/** procesamiento del mensaje `auth' */
static unsigned auth_process(const struct auth_st * d) {
unsigned ret = AUTH_WRITE;
uint8_t v = d->verified;
const uint8_t r = v ? 0x00 : 0xFE;
if (auth_marshall(d->wb, (enum socks_response_status) r) == -1) {
ret = ERROR;
}
return ret;
}
static void auth_read_close(const unsigned state, struct selector_key * key) {
struct auth_st * d = &ATTACHMENT(key)->client.auth;
auth_parser_close(&d->parser);
}
static unsigned auth_write(struct selector_key * key) {
struct auth_st * d = &ATTACHMENT(key)->client.auth;
unsigned ret = AUTH_WRITE;
uint8_t * ptr;
size_t count;
ssize_t n;
ptr = buffer_read_ptr(d->wb, &count);
n = send(key->fd, ptr, count, MSG_NOSIGNAL);
if (n == -1) {
@ -367,6 +502,11 @@ static unsigned hello_write(struct selector_key * key) {
return ret;
}
////////////////////////////////////////////////////////////////////////////////
// REQUEST
////////////////////////////////////////////////////////////////////////////////
static void request_init(const unsigned state, struct selector_key * key) {
struct request_st * d = &ATTACHMENT(key)->client.request;
@ -756,6 +896,14 @@ static const struct state_definition client_statbl[] = {
}, {
.state = HELLO_WRITE,
.on_write_ready = hello_write,
}, {
.state = AUTH_READ,
.on_arrival = auth_read_init,
.on_departure = auth_read_close,
.on_read_ready = auth_read,
}, {
.state = AUTH_WRITE,
.on_write_ready = auth_write,
}, {
.state = REQUEST_READ,
.on_arrival = request_init,