Archived
1
0
Fork 0

Working bot answering PING, and its C wrapper.

This commit is contained in:
Julien Dessaux 2016-08-11 13:20:39 +02:00
parent 462de88e8b
commit cffeb8a355
12 changed files with 475 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
src/*.d
src/*.o
.*.swp

62
GNUmakefile Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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