Working bot answering PING, and its C wrapper.
This commit is contained in:
parent
462de88e8b
commit
cffeb8a355
12 changed files with 475 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
src/*.d
|
||||||
|
src/*.o
|
||||||
|
.*.swp
|
62
GNUmakefile
Normal file
62
GNUmakefile
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
# Common
|
||||||
|
prefix= /usr/local
|
||||||
|
|
||||||
|
CC=clang
|
||||||
|
CFLAGS= -Wall -Werror -Wextra
|
||||||
|
#CFLAGS+= -std=c99
|
||||||
|
CFLAGS+= -std=gnu99
|
||||||
|
#CFLAGS+= -Os
|
||||||
|
CFLAGS+= -g
|
||||||
|
|
||||||
|
LDFLAGS= -lgnutls -lpthread -ldl -rdynamic
|
||||||
|
LDFLAGS+= -g
|
||||||
|
#LDFLAGS+= -Os
|
||||||
|
|
||||||
|
# Target: cbot
|
||||||
|
cbot_BIN= cbot
|
||||||
|
cbot_OBJ= $(subst .c,.o,$(wildcard src/*.c))
|
||||||
|
|
||||||
|
# Targets: plugins
|
||||||
|
plugins_SO= $(subst .c,.so,$(wildcard src/plugin/*.c))
|
||||||
|
|
||||||
|
# Rules
|
||||||
|
all: $(cbot_BIN) $(plugins_SO)
|
||||||
|
|
||||||
|
$(cbot_BIN): $(cbot_OBJ)
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) -o $(cbot_BIN) $(cbot_OBJ)
|
||||||
|
|
||||||
|
%.so: %.c
|
||||||
|
$(CC) -shared -nostartfiles -fPIC $(CFLAGS) $*.c -o $*.so
|
||||||
|
$(CC) -MM $(CFLAGS) $*.c > $*.d
|
||||||
|
@mv -f $*.d $*.d.tmp
|
||||||
|
@sed -e 's|.*:|$*.o:|' < $*.d.tmp > $*.d
|
||||||
|
@sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $*.d
|
||||||
|
@rm -f $*.d.tmp
|
||||||
|
|
||||||
|
-include $(cbot_OBJ:.o=.d)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) -c $(CFLAGS) $*.c -o $*.o
|
||||||
|
$(CC) -MM $(CFLAGS) $*.c > $*.d
|
||||||
|
@mv -f $*.d $*.d.tmp
|
||||||
|
@sed -e 's|.*:|$*.o:|' < $*.d.tmp > $*.d
|
||||||
|
@sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $*.d
|
||||||
|
@rm -f $*.d.tmp
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) */*.o */*.d */*/*.o */*/*.d */*/*.so
|
||||||
|
$(RM) $(cbot_BIN) AllTests
|
||||||
|
$(RM) -rf clang-analyzer
|
||||||
|
|
||||||
|
check:
|
||||||
|
scan-build -o clang-analyzer make
|
||||||
|
clang -o AllTests $(wildcard tests/*.c)
|
||||||
|
./AllTests
|
||||||
|
|
||||||
|
tags:
|
||||||
|
ctags -o .tags -a $(wildcard src/*.[hc])
|
||||||
|
|
||||||
|
.PHONY: all clean install uninstall tags
|
||||||
|
|
||||||
|
start:
|
||||||
|
./cbot
|
11
README
Normal file
11
README
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
Dependencies :
|
||||||
|
- Fungi : cabal install fungi
|
||||||
|
- gnutls-devel
|
||||||
|
|
||||||
|
Configuring :
|
||||||
|
- irc server and port : src/config.h
|
||||||
|
- password and NICK in bot.b98
|
||||||
|
|
||||||
|
Running the bot :
|
||||||
|
- make
|
||||||
|
- ./cbot
|
3
bot.b98
Normal file
3
bot.b98
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<>>#;>:#,_v;"PASS my_secrets"da"NICK b98"da"USER b98 0 0 b98"da"JOIN #test"da
|
||||||
|
>~'P-#v_~'I-#v_~'N-#v_~'G-#v_ad"uoarg GNOP"ck,v
|
||||||
|
^ < < < < <
|
112
src/b98.c
Normal file
112
src/b98.c
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "b98.h"
|
||||||
|
#include "tls.h"
|
||||||
|
|
||||||
|
pid_t pid = 0;
|
||||||
|
int from_b98[2];
|
||||||
|
int to_b98[2];
|
||||||
|
|
||||||
|
#define PARENT_READ from_b98[0]
|
||||||
|
#define CHILD_WRITE from_b98[1]
|
||||||
|
#define CHILD_READ to_b98[0]
|
||||||
|
#define PARENT_WRITE to_b98[1]
|
||||||
|
|
||||||
|
int bot_init(void)
|
||||||
|
{
|
||||||
|
if (pipe (from_b98)) {
|
||||||
|
perror("from_b98 pipe");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (pipe (to_b98)) {
|
||||||
|
perror("to_b98 pipe");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
pid = fork ();
|
||||||
|
if (pid == (pid_t) 0) { // Child process
|
||||||
|
close(PARENT_WRITE);
|
||||||
|
close(PARENT_READ);
|
||||||
|
|
||||||
|
dup2(CHILD_READ, STDIN_FILENO); close(CHILD_READ);
|
||||||
|
dup2(CHILD_WRITE, STDOUT_FILENO); close(CHILD_WRITE);
|
||||||
|
|
||||||
|
execvp("fungi", (char *[]) { "fungi", "bot.b98" } );
|
||||||
|
perror("execv");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if (pid < (pid_t) 0) {
|
||||||
|
fprintf (stderr, "Fork failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else { // Parent process
|
||||||
|
close(CHILD_READ);
|
||||||
|
close(CHILD_WRITE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int bot_loop(int socket_fd)
|
||||||
|
{
|
||||||
|
fd_set rfds;
|
||||||
|
while (1) {
|
||||||
|
struct timeval timeout;
|
||||||
|
timeout.tv_sec = 1;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_SET(PARENT_READ, &rfds);
|
||||||
|
FD_SET(socket_fd, &rfds);
|
||||||
|
select(20, &rfds, NULL, NULL, &timeout);
|
||||||
|
if ( FD_ISSET(PARENT_READ, &rfds) ) {
|
||||||
|
static char sbuf[512]; /* string buffer */
|
||||||
|
static int n = 0; /* nition in buffer */
|
||||||
|
ssize_t len = n + read(PARENT_READ, sbuf + n, 512 - n);
|
||||||
|
while (n < len) {
|
||||||
|
if ((n > 0 && sbuf[n] == '\n' && sbuf[n - 1] == '\r') ) { /* If we got a full message */
|
||||||
|
tls_send(sbuf, n);
|
||||||
|
sbuf[n - 1] = '\0';
|
||||||
|
printf(">>> %s\n", sbuf);
|
||||||
|
memmove(sbuf, sbuf + n + 1, len - n - 1);
|
||||||
|
len = len - n - 1;
|
||||||
|
if (len == 1)
|
||||||
|
len = 0;
|
||||||
|
n = 0; /* we reinitialise n for the next run */
|
||||||
|
} else if (n == 512) { /* If we got a full buffer without finding a \r\n */
|
||||||
|
fprintf(stderr, "We got a full buffer without finding a \r\n");
|
||||||
|
return 2;
|
||||||
|
} else { /* Nothing to do otherwise but roll the next char */
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ( FD_ISSET(socket_fd, &rfds) ) {
|
||||||
|
tls_read(&bot_handle_input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int bot_handle_input(const char* const sbuf, int len)
|
||||||
|
{
|
||||||
|
static char msg[512];
|
||||||
|
static int pos = 0; /* position in msg buffer */
|
||||||
|
for (int n = 0; n < len; n++) {
|
||||||
|
msg[pos] = sbuf[n];
|
||||||
|
if ((n > 0 && sbuf[n] == '\n' && sbuf[n - 1] == '\r') ) { /* If we got a full message */
|
||||||
|
write(PARENT_WRITE, msg, n);
|
||||||
|
msg[pos - 1] = '\0';
|
||||||
|
printf("<<< %s\n", msg);
|
||||||
|
pos = 0; /* we reinitialise pos for the next run */
|
||||||
|
} else if (pos == sizeof(msg)) { /* If we got a full buffer without finding a \r\n */
|
||||||
|
fprintf(stderr, "We got a full buffer without finding a \r\n");
|
||||||
|
return 2;
|
||||||
|
} else { /* Nothing to do otherwise but roll the next char */
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
8
src/b98.h
Normal file
8
src/b98.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef _B98_H_
|
||||||
|
#define _B98_H_
|
||||||
|
|
||||||
|
int bot_init(void);
|
||||||
|
int bot_loop(int socket_fd);
|
||||||
|
int bot_handle_input(const char* const sbuf, int len);
|
||||||
|
|
||||||
|
#endif
|
18
src/bot.c
Normal file
18
src/bot.c
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#include "b98.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "tls.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int err = bot_init();
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
tls_init();
|
||||||
|
int socket_fd = tls_connect(HOST, PORT);
|
||||||
|
|
||||||
|
bot_loop(socket_fd);
|
||||||
|
|
||||||
|
tls_clean();
|
||||||
|
return 0;
|
||||||
|
}
|
7
src/config.h
Normal file
7
src/config.h
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef _CONFIG_H_
|
||||||
|
#define _CONFIG_H_
|
||||||
|
|
||||||
|
#define HOST "example.com"
|
||||||
|
#define PORT 6667
|
||||||
|
|
||||||
|
#endif
|
65
src/tcp.c
Normal file
65
src/tcp.c
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "tcp.h"
|
||||||
|
|
||||||
|
int tcp_connect(const char* const host, const int port)
|
||||||
|
{
|
||||||
|
char ip[INET6_ADDRSTRLEN];
|
||||||
|
hostname_to_ip(host , ip);
|
||||||
|
printf("%s resolved to %s\n" , host, ip);
|
||||||
|
|
||||||
|
struct sockaddr_in sa;
|
||||||
|
int sd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
memset(&sa, '\0', sizeof(sa));
|
||||||
|
sa.sin_family = AF_INET;
|
||||||
|
sa.sin_port = htons(port);
|
||||||
|
inet_pton(AF_INET, ip, &sa.sin_addr);
|
||||||
|
|
||||||
|
int err = connect(sd, (struct sockaddr *) &sa, sizeof(sa));
|
||||||
|
if (err < 0) {
|
||||||
|
fprintf(stderr, "Couldn't connect %s:%d : %s\n", host, port, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tcp_close(const int sd)
|
||||||
|
{
|
||||||
|
shutdown(sd, SHUT_RDWR);
|
||||||
|
close(sd);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hostname_to_ip(const char* const hostname, char *ip)
|
||||||
|
{
|
||||||
|
struct addrinfo hints, *servinfo; //, *p;
|
||||||
|
struct sockaddr_in *h;
|
||||||
|
int rv, ret = 0;
|
||||||
|
|
||||||
|
memset(&hints, 0, sizeof hints);
|
||||||
|
hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
|
if ( (rv = getaddrinfo( hostname , "http" , &hints , &servinfo)) != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
|
||||||
|
ret = 1;
|
||||||
|
} else {
|
||||||
|
//// loop through all the results and connect to the first we can
|
||||||
|
//for(p = servinfo; p != NULL; p = p->ai_next)
|
||||||
|
//{
|
||||||
|
// h = (struct sockaddr_in *) p->ai_addr;
|
||||||
|
// strcpy(ip , inet_ntoa( h->sin_addr ) );
|
||||||
|
//}
|
||||||
|
h = (struct sockaddr_in *) servinfo->ai_addr;
|
||||||
|
strcpy(ip, inet_ntoa(h->sin_addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(servinfo); // all done with this structure
|
||||||
|
return ret;
|
||||||
|
}
|
8
src/tcp.h
Normal file
8
src/tcp.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef _TCP_H_
|
||||||
|
#define _TCP_H_
|
||||||
|
|
||||||
|
int hostname_to_ip(const char* const hostname, char *ip);
|
||||||
|
int tcp_connect(const char* const host, const int port);
|
||||||
|
void tcp_close(const int sd);
|
||||||
|
|
||||||
|
#endif
|
165
src/tls.c
Normal file
165
src/tls.c
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "tcp.h"
|
||||||
|
#include "tls.h"
|
||||||
|
|
||||||
|
int sd = 0;
|
||||||
|
gnutls_session_t session;
|
||||||
|
gnutls_certificate_credentials_t xcred;
|
||||||
|
|
||||||
|
void tls_init(void)
|
||||||
|
{
|
||||||
|
gnutls_global_init();
|
||||||
|
gnutls_certificate_allocate_credentials(&xcred);
|
||||||
|
//gnutls_certificate_set_x509_trust_file(xcred, cafile, GNUTLS_X509_FMT_PEM);
|
||||||
|
//gnutls_certificate_set_verify_function(xcred, _verify_certificate_callback);
|
||||||
|
//gnutls_certificate_set_x509_key_file (xcred, "cert.pem", "key.pem", GNUTLS_X509_FMT_PEM);
|
||||||
|
gnutls_init(&session, GNUTLS_CLIENT);
|
||||||
|
|
||||||
|
// Only usefull if accessing a virtual hosting server
|
||||||
|
//gnutls_session_set_ptr(session, (void *) "my_host_name");
|
||||||
|
//gnutls_server_name_set(session, GNUTLS_NAME_DNS, "my_host_name", strlen("my_host_name"));
|
||||||
|
|
||||||
|
gnutls_set_default_priority(session);
|
||||||
|
gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
|
||||||
|
}
|
||||||
|
|
||||||
|
int tls_connect(const char* const host, const unsigned short port)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
sd = tcp_connect(host, port);
|
||||||
|
if (sd < 0) {
|
||||||
|
tls_clean();
|
||||||
|
exit(101);
|
||||||
|
}
|
||||||
|
|
||||||
|
gnutls_transport_set_int(session, sd);
|
||||||
|
gnutls_handshake_set_timeout(session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
|
||||||
|
|
||||||
|
/* Perform the TLS handshake */
|
||||||
|
do {
|
||||||
|
ret = gnutls_handshake(session);
|
||||||
|
}
|
||||||
|
while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "TLS Handshake failed\n");
|
||||||
|
gnutls_perror(ret);
|
||||||
|
tls_clean();
|
||||||
|
exit(102);
|
||||||
|
} else {
|
||||||
|
char *desc;
|
||||||
|
|
||||||
|
desc = gnutls_session_get_desc(session);
|
||||||
|
printf("# TLS Session info: %s\n", desc);
|
||||||
|
gnutls_free(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sf = fcntl (sd, F_GETFL, 0);
|
||||||
|
if (sf == -1) {
|
||||||
|
perror("fcntl get");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fcntl(sd, F_SETFL, sf | O_NONBLOCK);
|
||||||
|
if (sf == -1) {
|
||||||
|
perror("fcntl set");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tls_read(int (*input_handler_callback)(const char* const sbuf, const int len))
|
||||||
|
{
|
||||||
|
char sbuf[512]; /* string buffer */
|
||||||
|
int len = 0; /* len read from the server */
|
||||||
|
len = gnutls_record_recv(session, sbuf, sizeof(sbuf));
|
||||||
|
if (len == 0) {
|
||||||
|
printf("Server has closed the TLS connection\n");
|
||||||
|
tls_clean();
|
||||||
|
return 1;
|
||||||
|
} else if (len < 0 && gnutls_error_is_fatal(len) == 0) {
|
||||||
|
fprintf(stderr, "*** Warning: %s\n", gnutls_strerror(len));
|
||||||
|
return 3;
|
||||||
|
} else if (len < 0) {
|
||||||
|
fprintf(stderr, "*** Error: %s\n", gnutls_strerror(len));
|
||||||
|
tls_clean();
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
if (input_handler_callback(sbuf, len) != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tls_send(char *buf, int len)
|
||||||
|
{
|
||||||
|
gnutls_record_send(session, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tls_close(void) {
|
||||||
|
gnutls_bye(session, GNUTLS_SHUT_RDWR);
|
||||||
|
tls_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tls_clean(void)
|
||||||
|
{
|
||||||
|
tcp_close(sd);
|
||||||
|
gnutls_deinit(session);
|
||||||
|
gnutls_certificate_free_credentials(xcred);
|
||||||
|
gnutls_global_deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function will verify the peer's certificate, and check
|
||||||
|
* if the hostname matches, as well as the activation, expiration dates. */
|
||||||
|
//static int _verify_certificate_callback(gnutls_session_t session)
|
||||||
|
//{
|
||||||
|
// unsigned int status;
|
||||||
|
// int ret, type;
|
||||||
|
// const char *hostname;
|
||||||
|
// gnutls_datum_t out;
|
||||||
|
//
|
||||||
|
// /* read hostname */
|
||||||
|
// hostname = gnutls_session_get_ptr(session);
|
||||||
|
//
|
||||||
|
// /* This verification function uses the trusted CAs in the credentials
|
||||||
|
// * structure. So you must have installed one or more CA certificates. */
|
||||||
|
// gnutls_typed_vdata_st data[2];
|
||||||
|
//
|
||||||
|
// memset(data, 0, sizeof(data));
|
||||||
|
//
|
||||||
|
// data[0].type = GNUTLS_DT_DNS_HOSTNAME;
|
||||||
|
// data[0].data = (void*)hostname;
|
||||||
|
//
|
||||||
|
// data[1].type = GNUTLS_DT_KEY_PURPOSE_OID;
|
||||||
|
// data[1].data = (void*)GNUTLS_KP_TLS_WWW_SERVER;
|
||||||
|
//
|
||||||
|
// ret = gnutls_certificate_verify_peers(session, data, 2, &status);
|
||||||
|
// if (ret < 0) {
|
||||||
|
// printf("Error\n");
|
||||||
|
// return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// type = gnutls_certificate_type_get(session);
|
||||||
|
//
|
||||||
|
// ret = gnutls_certificate_verification_status_print(status, type, &out, 0);
|
||||||
|
// if (ret < 0) {
|
||||||
|
// printf("Error\n");
|
||||||
|
// return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// printf("%s", out.data);
|
||||||
|
//
|
||||||
|
// gnutls_free(out.data);
|
||||||
|
//
|
||||||
|
// if (status != 0) /* Certificate is not trusted */
|
||||||
|
// return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
|
//
|
||||||
|
// /* notify gnutls to continue handshake normally */
|
||||||
|
// return 0;
|
||||||
|
//}
|
13
src/tls.h
Normal file
13
src/tls.h
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef _TLS_H_
|
||||||
|
#define _TLS_H_
|
||||||
|
|
||||||
|
#include <gnutls/gnutls.h>
|
||||||
|
|
||||||
|
void tls_init(void);
|
||||||
|
int tls_connect(const char* const host, const unsigned short port);
|
||||||
|
int tls_read(int (*input_handler_callback)(const char* const sbuf, const int len));
|
||||||
|
void tls_send(char *buf, int len);
|
||||||
|
void tls_close(void);
|
||||||
|
void tls_clean(void);
|
||||||
|
|
||||||
|
#endif
|
Reference in a new issue