1
0
forked from bip/bip

Compare commits

...

18 Commits

Author SHA1 Message Date
Loïc Gomez
12f228be25 Fix endless loop on /BIP LIST connections with long on_connect_send 2024-02-04 23:24:29 +09:00
Loïc Gomez
e382d31b67 Handle CAP requests from clients, reply with no capabilities 2024-02-04 22:45:22 +09:00
Loïc Gomez
7ab58b5b72 Add method to stringify a line struct, skipping first N elements 2024-02-04 22:44:27 +09:00
Loïc Gomez
8776eaf076 Merge branch '244_log_timestamp_format_plus_compilefixes' into fix_list_conn 2024-02-04 21:31:30 +09:00
Loïc Gomez
3cc93758ba Add timestamp_format global option 2024-02-04 16:17:11 +09:00
Loïc Gomez
8f71bc8c50 Add and use a default timestamp format for logging 2024-02-04 16:17:11 +09:00
Loïc Gomez
b0725acd05 Log without timestamp in specific situations (early/conf phase) 2024-02-04 15:58:48 +09:00
Loïc Gomez
771f664f02 Fix deprecation notices for OpenSSL 3 2024-02-04 15:58:48 +09:00
Loïc Gomez
d8e65ef890 Set default backlog_lines = 0 (fixes Debian bug #818374) 2024-02-04 15:35:30 +09:00
Loïc Gomez
bb0fec792a Update copyright info 2024-02-04 15:35:30 +09:00
Loïc Gomez
27cd17ea3b Move base64 files to a dedicated dir in utils/ 2024-02-04 15:35:30 +09:00
Loïc Gomez
91dea58db0 Be as close as possible to base64 lib, and use new function to reduce changes.
* use new base64_encode_no_lf() function having no line returns
* fix irc_server_sasl_authenticate(): add missing free(raw_str)
2024-02-04 15:35:30 +09:00
89976d2eb9 update base64 files: resync from the src project
The allocated buffer size was slightly overestimated due to unused line
feeds taken in account.

I kept the differences with the original project as small as possible in
order to make the next synchronization easy.
2024-02-04 15:35:30 +09:00
Loïc Gomez
f31e484bc1 Log unhandled IRC errors as LOG_INFO in bip.log 2024-02-04 15:35:30 +09:00
Loïc Gomez
e74198f0ef Set default recon_timer (and step) to 30s 2024-02-04 15:35:30 +09:00
Loïc Gomez
3a8d4283a5 Allow a user to /BIP JUMP [-f] [other_conn] (within their list).
Also:
* fix message when JUMPing on some already reconnecting link
* add find_link() method
* add reconnect timer info if any (else display 0s)
* add -f flag to reset reconnect timer
2024-02-04 15:35:30 +09:00
Loïc Gomez
d95b7a073a Fix file descriptor leak on erroring client disconnects 2024-02-04 01:10:24 +09:00
Loïc Gomez
cd30ff7ea6 Fix deprecation notices for OpenSSL 3 2024-02-04 01:10:24 +09:00
25 changed files with 394 additions and 135 deletions

2
.gitignore vendored
View File

@ -58,6 +58,8 @@ src/lex.c
# Binaries # Binaries
src/*.o src/*.o
src/utils/*.o
src/utils/*/*.o
src/*.a src/*.a
src/bip src/bip
src/bipmkpw src/bipmkpw

View File

@ -10,4 +10,6 @@ Thanks to our marketting and management team:
ack|, ato, blackmore, lafouine, Gaston & gromit ack|, ato, blackmore, lafouine, Gaston & gromit
Crypto shamelessly stolen from Christophe 'sexy' Devine. Crypto shamelessly stolen from Christophe 'sexy' Devine.
Credits to Jouni Malinen for base64 library (http://web.mit.edu/freebsd/head/contrib/wpa/src/utils/)
Credits to Jouni Malinen for base64 library:
Source: http://w1.fi/cgit/hostap/commit/src/utils/base64.c

View File

@ -1,4 +1,4 @@
bip copyright 2004 2005 Arnaud Cornet and Loïc Gomez, is released under the BIP copyright 2004-2022 Arnaud Cornet and Loïc Gomez, is released under the
terms of the GNU GPL, License that you can find below. terms of the GNU GPL, License that you can find below.
Specific permission is granted for the GPLed code in this distribution to Specific permission is granted for the GPLed code in this distribution to

2
README
View File

@ -220,5 +220,5 @@ IV. USING BIP
Happy ircing! Happy ircing!
-- Arnaud Cornet <nohar@t1r.net> and Loïc Gomez <opensource@kyoshiro.org> -- Arnaud Cornet <nohar@t1r.net> and Loïc Gomez <bip@kyoshiro.org>

View File

@ -146,7 +146,7 @@ Defines the delay between each logfiles sync to the disk. Must be a non null
positive integer. positive integer.
.TP .TP
\fBreconn_timer\fP (default: \fB120\fP) \fBreconn_timer\fP (default: \fB30\fP)
Defines the initial delay (in seconds) before a reconnection attempt. Defines the initial delay (in seconds) before a reconnection attempt.
The delay increases with the number of attempts: The delay increases with the number of attempts:
delay = reconn_timer * number of attempts delay = reconn_timer * number of attempts
@ -227,7 +227,7 @@ This option should of course not be enabled if \fBbacklog_lines\fP is 0 !
If you still want to do so, don't forget to \fB/BIP BLRESET\fP sometimes. If you still want to do so, don't forget to \fB/BIP BLRESET\fP sometimes.
.TP .TP
\fBbacklog_lines\fP (default: \fB10\fP) \fBbacklog_lines\fP (default: \fB0\fP)
If set to 0, BIP will replay all the logs since last client disconnect. Else, If set to 0, BIP will replay all the logs since last client disconnect. Else,
it'll replay exactly \fBbacklog_lines\fP lines on each channel and privates. it'll replay exactly \fBbacklog_lines\fP lines on each channel and privates.
Be aware that BIP will replay \fBbacklog_lines\fP lines of all privates, even Be aware that BIP will replay \fBbacklog_lines\fP lines of all privates, even

View File

@ -86,7 +86,7 @@
# Sets the initial delay (in seconds) before a reconnection attempt. # Sets the initial delay (in seconds) before a reconnection attempt.
# The delay increases with the number of attempts: # The delay increases with the number of attempts:
# delay = reconn_timer * number of attempts # delay = reconn_timer * number of attempts
#reconn_timer = 120; #reconn_timer = 30;
# Network definition, a name and server info # Network definition, a name and server info
#network { #network {

View File

@ -19,7 +19,7 @@ libbip_a_SOURCES = \
path_util.c path_util.h \ path_util.c path_util.h \
tuple.h \ tuple.h \
util.c util.h \ util.c util.h \
utils/base64.c utils/base64.h utils/b64/base64.c utils/b64/base64.h
libbip_a_CFLAGS = ${OPENSSL_CFLAGS} $(AM_CFLAGS) libbip_a_CFLAGS = ${OPENSSL_CFLAGS} $(AM_CFLAGS)

104
src/bip.c
View File

@ -39,6 +39,7 @@ int sighup = 0;
char *conf_log_root; char *conf_log_root;
char *conf_log_format; char *conf_log_format;
char *conf_timestamp_format;
int conf_log_level; int conf_log_level;
char *conf_ip; char *conf_ip;
unsigned short conf_port; unsigned short conf_port;
@ -67,6 +68,7 @@ void conf_die(bip_t *bip, char *fmt, ...);
static char *get_tuple_pvalue(list_t *tuple_l, int lex); static char *get_tuple_pvalue(list_t *tuple_l, int lex);
void bip_notify(struct link_client *ic, char *fmt, ...); void bip_notify(struct link_client *ic, char *fmt, ...);
void adm_list_connections(struct link_client *ic, struct bipuser *bu); void adm_list_connections(struct link_client *ic, struct bipuser *bu);
struct link *find_link(const char *link_name, struct bipuser *bu);
void free_conf(list_t *l); void free_conf(list_t *l);
@ -160,7 +162,8 @@ void conf_die(bip_t *bip, char *fmt, ...)
error = bip_realloc(error, size); error = bip_realloc(error, size);
} }
va_start(ap, fmt); va_start(ap, fmt);
_mylog(LOG_ERROR, fmt, ap); /* log timestamp format is a conf option, we cannot log with time */
mylog_nots(LOG_ERROR, fmt, ap);
va_end(ap); va_end(ap);
} }
@ -373,6 +376,45 @@ static int add_network(bip_t *bip, list_t *data)
return 1; return 1;
} }
void adm_bip_jump(struct link_client *ic, const char *conn_name,
int reset_timer)
{
struct link *lnk;
if (conn_name) {
lnk = find_link(conn_name, LINK(ic)->user);
if (!lnk) {
bip_notify(ic, "-- Cannot find connection named %s",
conn_name);
return;
}
goto do_the_jump;
} else {
lnk = LINK(ic);
}
do_the_jump:
if (!lnk->l_server) {
if (reset_timer) {
lnk->recon_timer = 0;
bip_notify(ic,
"-- %s is not connected, "
"timer has been reset, please wait",
conn_name);
return;
}
int timer = (lnk->recon_timer ? lnk->recon_timer : 0);
bip_notify(ic,
"-- %s is not connected, "
"please wait for reconnect (%ds)",
conn_name, timer);
return;
}
WRITE_LINE1(CONN(lnk->l_server), NULL, "QUIT", "jumpin' jumpin'");
connection_close(CONN(lnk->l_server));
bip_notify(ic, "-- Jumping to next server on %s", conn_name);
}
void adm_bip_delconn(bip_t *bip, struct link_client *ic, const char *conn_name) void adm_bip_delconn(bip_t *bip, struct link_client *ic, const char *conn_name)
{ {
struct bipuser *user = LINK(ic)->user; struct bipuser *user = LINK(ic)->user;
@ -664,6 +706,26 @@ static int add_connection(bip_t *bip, struct bipuser *user, list_t *data)
return 1; return 1;
} }
struct link *find_link(const char *link_name, struct bipuser *bu)
{
list_iterator_t it;
for (list_it_init(&_bip->link_list, &it); list_it_item(&it);
list_it_next(&it)) {
struct link *lnk = list_it_item(&it);
if (!lnk)
continue;
// Only lookup in optionally specified user links
if (bu && bu != lnk->user)
continue;
if (strcmp(link_name, lnk->name) == 0)
return lnk;
}
return NULL;
}
static char *get_tuple_pvalue(list_t *tuple_l, int lex) static char *get_tuple_pvalue(list_t *tuple_l, int lex)
{ {
struct tuple *t; struct tuple *t;
@ -857,6 +919,7 @@ static int validate_config(bip_t *bip)
struct bipuser *user; struct bipuser *user;
struct link *link; struct link *link;
struct chan_info *ci; struct chan_info *ci;
size_t len;
int r = 1; int r = 1;
for (hash_it_init(&bip->users, &it); (user = hash_it_item(&it)); for (hash_it_init(&bip->users, &it); (user = hash_it_item(&it));
@ -921,6 +984,10 @@ static int validate_config(bip_t *bip)
} }
} }
} }
len = strlen(conf_timestamp_format);
if (len > TIMESTAMP_FORMAT_MAXLEN)
fatal("Timestamp format length exceeds maximum allowed length(%d)", TIMESTAMP_FORMAT_MAXLEN);
return r; return r;
} }
@ -1035,6 +1102,9 @@ int fireup(bip_t *bip, FILE *conf)
case LEX_LOG_LEVEL: case LEX_LOG_LEVEL:
conf_log_level = t->ndata; conf_log_level = t->ndata;
break; break;
case LEX_TIMESTAMP_FORMAT:
MOVE_STRING(conf_timestamp_format, t->pdata);
break;
case LEX_IP: case LEX_IP:
MOVE_STRING(conf_ip, t->pdata); MOVE_STRING(conf_ip, t->pdata);
break; break;
@ -1331,9 +1401,10 @@ void adm_print_connection(struct link_client *ic, struct link *lnk,
bufpos = buf; bufpos = buf;
list_iterator_t itocs; list_iterator_t itocs;
int i = 0;
for (list_it_init(&lnk->on_connect_send, &itocs); for (list_it_init(&lnk->on_connect_send, &itocs);
list_it_item(&itocs);) { list_it_item(&itocs) && i < 10; i++) {
bufpos = bip_strcatf_fit(&remaining, bufpos, "%s", bufpos = bip_strcatf_fit(&remaining, bufpos, " on_connect_send: %s",
(char *)list_it_item(&itocs)); (char *)list_it_item(&itocs));
if (!bufpos) { if (!bufpos) {
// if oversized, print and reset // if oversized, print and reset
@ -1341,6 +1412,7 @@ void adm_print_connection(struct link_client *ic, struct link *lnk,
bip_notify(ic, "%s", buf); bip_notify(ic, "%s", buf);
remaining = LINE_SIZE_LIM; remaining = LINE_SIZE_LIM;
bufpos = buf; bufpos = buf;
list_it_next(&itocs);
continue; continue;
} else { } else {
// if ok, go to next item // if ok, go to next item
@ -1992,8 +2064,8 @@ void adm_bip_help(struct link_client *ic, int admin, const char *subhelp)
bip_notify(ic, "/BIP LIST networks|connections"); bip_notify(ic, "/BIP LIST networks|connections");
} }
bip_notify(ic, bip_notify(ic,
"/BIP JUMP # jump to next server (in same " "/BIP JUMP [-f] [conn_name] # jump to next "
"network)"); "server (defaults to current network)");
bip_notify(ic, bip_notify(ic,
"/BIP BLRESET [channel|query]# reset backlog " "/BIP BLRESET [channel|query]# reset backlog "
"(this connection only). Add -q flag and the " "(this connection only). Add -q flag and the "
@ -2046,8 +2118,14 @@ void adm_bip_help(struct link_client *ic, int admin, const char *subhelp)
" Removing a connection will cause " " Removing a connection will cause "
"its disconnection."); "its disconnection.");
} else if (strcasecmp(subhelp, "JUMP") == 0) { } else if (strcasecmp(subhelp, "JUMP") == 0) {
bip_notify(ic, "/BIP JUMP :"); bip_notify(ic, "/BIP JUMP [-f] [conn_name]:");
bip_notify(ic, " Jump to next server in current network."); bip_notify(ic, " Jump to next server in current network.");
bip_notify(ic,
" If conn_name is set, jump to next server in "
"conn_name network instead.");
bip_notify(ic,
" If -f flag is used, also resets reconnect "
"timer.");
} else if (strcasecmp(subhelp, "BLRESET") == 0) { } else if (strcasecmp(subhelp, "BLRESET") == 0) {
bip_notify(ic, "/BIP BLRESET :"); bip_notify(ic, "/BIP BLRESET :");
bip_notify(ic, " Reset backlog on this network."); bip_notify(ic, " Reset backlog on this network.");
@ -2227,12 +2305,16 @@ int adm_bip(bip_t *bip, struct link_client *ic, struct line *line, int privmsg)
bip_notify(ic, "-- Invalid INFO request"); bip_notify(ic, "-- Invalid INFO request");
} }
} else if (irc_line_elem_case_equals(line, privmsg + 1, "JUMP")) { } else if (irc_line_elem_case_equals(line, privmsg + 1, "JUMP")) {
if (LINK(ic)->l_server) { if (irc_line_count(line) == privmsg + 2) {
WRITE_LINE1(CONN(LINK(ic)->l_server), NULL, "QUIT", adm_bip_jump(ic, NULL, 0);
"jumpin' jumpin'"); } else if (irc_line_count(line) == privmsg + 3) {
connection_close(CONN(LINK(ic)->l_server)); adm_bip_jump(ic, irc_line_elem(line, privmsg + 2), 0);
} else if (irc_line_count(line) == privmsg + 4
&& irc_line_elem_equals(line, privmsg + 2, "-f")) {
adm_bip_jump(ic, irc_line_elem(line, privmsg + 3), 1);
} else {
bip_notify(ic, "-- Invalid usage (/BIP HELP JUMP)");
} }
bip_notify(ic, "-- Jumping to next server");
} else if (irc_line_elem_case_equals(line, privmsg + 1, "BLRESET")) { } else if (irc_line_elem_case_equals(line, privmsg + 1, "BLRESET")) {
if (irc_line_includes(line, privmsg + 2)) { if (irc_line_includes(line, privmsg + 2)) {
if (irc_line_elem_equals(line, privmsg + 2, "-q")) { if (irc_line_elem_equals(line, privmsg + 2, "-q")) {

View File

@ -39,6 +39,7 @@
extern int sighup; extern int sighup;
extern char *conf_log_root; extern char *conf_log_root;
extern char *conf_log_format; extern char *conf_log_format;
extern char *conf_timestamp_format;
extern int conf_log_level; extern int conf_log_level;
extern char *conf_ip; extern char *conf_ip;
extern unsigned short conf_port; extern unsigned short conf_port;
@ -170,6 +171,7 @@ int main(int argc, char **argv)
conf_log_root = NULL; conf_log_root = NULL;
conf_log_format = bip_strdup(DEFAULT_LOG_FORMAT); conf_log_format = bip_strdup(DEFAULT_LOG_FORMAT);
conf_log_level = DEFAULT_LOG_LEVEL; conf_log_level = DEFAULT_LOG_LEVEL;
conf_timestamp_format = bip_strdup(DEFAULT_TIMESTAMP_FORMAT);
conf_reconn_timer = DEFAULT_RECONN_TIMER; conf_reconn_timer = DEFAULT_RECONN_TIMER;
conf_daemonize = 1; conf_daemonize = 1;
conf_global_log_file = stderr; conf_global_log_file = stderr;

View File

@ -69,7 +69,7 @@ struct tuple *tuple_l_new(int type, void *p)
%} %}
%token LEX_IP LEX_EQ LEX_PORT LEX_CSS LEX_SEMICOLON LEX_CONNECTION LEX_NETWORK LEX_LBRA LEX_RBRA LEX_USER LEX_NAME LEX_NICK LEX_SERVER LEX_PASSWORD LEX_SRCIP LEX_HOST LEX_VHOST LEX_SOURCE_PORT LEX_NONE LEX_COMMENT LEX_BUNCH LEX_REALNAME LEX_SSL LEX_SSL_CHECK_MODE LEX_SSL_CHECK_STORE LEX_SSL_CLIENT_CERTFILE LEX_CIPHERS LEX_CSS_CIPHERS LEX_DEFAULT_CIPHERS LEX_DH_PARAM LEX_CHANNEL LEX_KEY LEX_LOG_ROOT LEX_LOG_FORMAT LEX_LOG_LEVEL LEX_BACKLOG_LINES LEX_BACKLOG_TIMESTAMP LEX_BACKLOG_NO_TIMESTAMP LEX_BACKLOG LEX_LOG LEX_LOG_SYSTEM LEX_LOG_SYNC_INTERVAL LEX_FOLLOW_NICK LEX_ON_CONNECT_SEND LEX_AWAY_NICK LEX_PID_FILE LEX_WRITE_OIDENTD LEX_OIDENTD_FILE LEX_IGN_FIRST_NICK LEX_ALWAYS_BACKLOG LEX_BLRESET_ON_TALK LEX_BLRESET_CONNECTION LEX_DEFAULT_USER LEX_DEFAULT_NICK LEX_DEFAULT_REALNAME LEX_NO_CLIENT_AWAY_MSG LEX_BL_MSG_ONLY LEX_ADMIN LEX_BIP_USE_NOTICE LEX_CSS_PEM LEX_AUTOJOIN_ON_KICK LEX_IGNORE_CAPAB LEX_RECONN_TIMER LEX_SASL_USERNAME LEX_SASL_PASSWORD LEX_SASL_MECHANISM %token LEX_IP LEX_EQ LEX_PORT LEX_CSS LEX_SEMICOLON LEX_CONNECTION LEX_NETWORK LEX_LBRA LEX_RBRA LEX_USER LEX_NAME LEX_NICK LEX_SERVER LEX_PASSWORD LEX_SRCIP LEX_HOST LEX_VHOST LEX_SOURCE_PORT LEX_NONE LEX_COMMENT LEX_BUNCH LEX_REALNAME LEX_SSL LEX_SSL_CHECK_MODE LEX_SSL_CHECK_STORE LEX_SSL_CLIENT_CERTFILE LEX_CIPHERS LEX_CSS_CIPHERS LEX_DEFAULT_CIPHERS LEX_DH_PARAM LEX_CHANNEL LEX_KEY LEX_LOG_ROOT LEX_LOG_FORMAT LEX_LOG_LEVEL LEX_BACKLOG_LINES LEX_BACKLOG_TIMESTAMP LEX_BACKLOG_NO_TIMESTAMP LEX_BACKLOG LEX_LOG LEX_LOG_SYSTEM LEX_LOG_SYNC_INTERVAL LEX_FOLLOW_NICK LEX_ON_CONNECT_SEND LEX_AWAY_NICK LEX_PID_FILE LEX_WRITE_OIDENTD LEX_OIDENTD_FILE LEX_IGN_FIRST_NICK LEX_ALWAYS_BACKLOG LEX_BLRESET_ON_TALK LEX_BLRESET_CONNECTION LEX_DEFAULT_USER LEX_DEFAULT_NICK LEX_DEFAULT_REALNAME LEX_NO_CLIENT_AWAY_MSG LEX_BL_MSG_ONLY LEX_ADMIN LEX_BIP_USE_NOTICE LEX_CSS_PEM LEX_AUTOJOIN_ON_KICK LEX_IGNORE_CAPAB LEX_RECONN_TIMER LEX_SASL_USERNAME LEX_SASL_PASSWORD LEX_SASL_MECHANISM LEX_TIMESTAMP_FORMAT
%union { %union {
int number; int number;
@ -108,6 +108,7 @@ command:
| LEX_LOG_SYNC_INTERVAL LEX_EQ LEX_INT { $$ = tuple_i_new( | LEX_LOG_SYNC_INTERVAL LEX_EQ LEX_INT { $$ = tuple_i_new(
LEX_LOG_SYNC_INTERVAL, $3); } LEX_LOG_SYNC_INTERVAL, $3); }
| LEX_PID_FILE LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_PID_FILE, $3); } | LEX_PID_FILE LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_PID_FILE, $3); }
| LEX_TIMESTAMP_FORMAT LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_TIMESTAMP_FORMAT, $3); }
| LEX_WRITE_OIDENTD LEX_EQ LEX_BOOL { $$ = tuple_i_new(LEX_WRITE_OIDENTD, $3); } | LEX_WRITE_OIDENTD LEX_EQ LEX_BOOL { $$ = tuple_i_new(LEX_WRITE_OIDENTD, $3); }
| LEX_OIDENTD_FILE LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_OIDENTD_FILE, $3); } | LEX_OIDENTD_FILE LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_OIDENTD_FILE, $3); }
/* deprecated */ /* deprecated */

View File

@ -62,7 +62,7 @@ void connection_close(connection_t *cn)
{ {
mylog(LOG_DEBUG, "Connection close asked. FD:%d (status: %d)", mylog(LOG_DEBUG, "Connection close asked. FD:%d (status: %d)",
(long)cn->handle, cn->connected); (long)cn->handle, cn->connected);
if (cn->connected != CONN_DISCONN && cn->connected != CONN_ERROR) { if (cn->connected != CONN_DISCONN) {
cn->connected = CONN_DISCONN; cn->connected = CONN_DISCONN;
if (close(cn->handle) == -1) if (close(cn->handle) == -1)
mylog(LOG_WARN, "Error on socket close: %s", mylog(LOG_WARN, "Error on socket close: %s",
@ -1171,6 +1171,7 @@ static connection_t *connection_init(int anti_flood, int ssl, time_t timeout,
static int ctx_set_dh(SSL_CTX *ctx) static int ctx_set_dh(SSL_CTX *ctx)
{ {
/* Return ephemeral DH parameters. */ /* Return ephemeral DH parameters. */
#if OPENSSL_VERSION_NUMBER < 0x30000000L /* 3.0.0 */
DH *dh = NULL; DH *dh = NULL;
FILE *f; FILE *f;
long ret; long ret;
@ -1203,7 +1204,31 @@ static int ctx_set_dh(SSL_CTX *ctx)
ERR_error_string(ERR_get_error(), NULL)); ERR_error_string(ERR_get_error(), NULL));
return 0; return 0;
} }
#else
BIO *pbio = BIO_new_file(conf_client_dh_file, "r");
if (!pbio) {
mylog(LOG_ERROR, "Unable to open DH parameters, BIO_new_file(%s): %s",
conf_client_dh_file, ERR_error_string(ERR_get_error(), NULL));
return 0;
}
EVP_PKEY *param = PEM_read_bio_Parameters(pbio, NULL);
BIO_free(pbio);
if (!param) {
mylog(LOG_ERROR, "TLS DH Error: PEM_read_bio_Parameters(%s): %s",
conf_client_dh_file, ERR_error_string(ERR_get_error(), NULL));
return 0;
}
if (SSL_CTX_set0_tmp_dh_pkey(ctx, param) != 1) {
EVP_PKEY_free(param);
mylog(LOG_ERROR, "TLS DH Error: SSL_CTX_set0_tmp_dh_pkey(%s): %s",
conf_client_dh_file, ERR_error_string(ERR_get_error(), NULL));
return 0;
}
#endif
mylog(LOG_DEBUG, "TLS: succesfully set up DH params %s",
conf_client_dh_file);
return 1; return 1;
} }
#endif #endif

View File

@ -19,7 +19,7 @@
#define DEFAULT_BACKLOG 1 #define DEFAULT_BACKLOG 1
#define DEFAULT_ALWAYS_BACKLOG 0 #define DEFAULT_ALWAYS_BACKLOG 0
#define DEFAULT_BL_MSG_ONLY 0 #define DEFAULT_BL_MSG_ONLY 0
#define DEFAULT_BACKLOG_LINES 10 #define DEFAULT_BACKLOG_LINES 0
#define DEFAULT_BACKLOG_TIMESTAMP BLTSTime #define DEFAULT_BACKLOG_TIMESTAMP BLTSTime
#define DEFAULT_BLRESET_ON_TALK 0 #define DEFAULT_BLRESET_ON_TALK 0
#define DEFAULT_BLRESET_CONNECTION 0 #define DEFAULT_BLRESET_CONNECTION 0
@ -28,7 +28,9 @@
#define DEFAULT_LOG_SYNC_INTERVAL 5 #define DEFAULT_LOG_SYNC_INTERVAL 5
#define DEFAULT_LOG_LEVEL LOG_INFO #define DEFAULT_LOG_LEVEL LOG_INFO
#define DEFAULT_LOG_FORMAT "%u/%n/%Y-%m/%c.%d.log" #define DEFAULT_LOG_FORMAT "%u/%n/%Y-%m/%c.%d.log"
/* keep dd-mm-yyyy as default since we used that before adding the option */
#define DEFAULT_TIMESTAMP_FORMAT "%d-%m-%Y %H:%M:%S"
#define DEFAULT_BIP_USE_NOTICE 0 #define DEFAULT_BIP_USE_NOTICE 0
#define DEFAULT_RECONN_TIMER 120 #define DEFAULT_RECONN_TIMER 30
#endif /* DEFAULTS_H */ #endif /* DEFAULTS_H */

View File

@ -22,7 +22,7 @@
#include "log.h" #include "log.h"
#include "connection.h" #include "connection.h"
#include "md5.h" #include "md5.h"
#include "utils/base64.h" #include "utils/b64/base64.h"
// TODO resolve assuming signed overflow does not occur when changing X +- C1 // TODO resolve assuming signed overflow does not occur when changing X +- C1
// cmp C2 to X cmp C2 -+ C1 // cmp C2 to X cmp C2 -+ C1
@ -33,6 +33,7 @@
extern int sighup; extern int sighup;
extern bip_t *_bip; extern bip_t *_bip;
static int irc_generic_error(struct link_server *server, struct line *line);
static int irc_join(struct link_server *server, struct line *line); static int irc_join(struct link_server *server, struct line *line);
static int irc_part(struct link_server *server, struct line *line); static int irc_part(struct link_server *server, struct line *line);
static int irc_mode(struct link_server *server, struct line *line); static int irc_mode(struct link_server *server, struct line *line);
@ -625,6 +626,10 @@ int irc_dispatch_server(bip_t *bip, struct link_server *server,
ret = irc_quit(server, line); ret = irc_quit(server, line);
} else if (irc_line_elem_equals(line, 0, "NICK")) { } else if (irc_line_elem_equals(line, 0, "NICK")) {
ret = irc_nick(server, line); ret = irc_nick(server, line);
} else if (irc_line_is_error(line)) {
// IRC errors catchall (for unhandled ones)
// logs error to bip.log
ret = irc_generic_error(server, line);
} }
if (ret == OK_COPY) { if (ret == OK_COPY) {
@ -1025,6 +1030,31 @@ static int irc_cli_pass(bip_t *bip, struct link_client *ic, struct line *line)
return OK_FORGET; return OK_FORGET;
} }
static int irc_cli_cap(bip_t *bip, struct link_client *ic, struct line *line)
{
if (irc_line_count(line) < 2)
return ERR_PROTOCOL;
/* When we decide to enable actual capabilities, we'll have to handle CAP
* version, add a flag cap_started or similar, and handle CAP END.
*/
if (irc_line_elem_equals(line, 1, "LS")) {
WRITE_LINE2(CONN(ic), NULL, "CAP", "*", "LS");
} else if (irc_line_elem_equals(line, 1, "LIST")) {
WRITE_LINE3(CONN(ic), NULL, "CAP", "*", "LIST", "");
} else if (irc_line_elem_equals(line, 1, "REQ")) {
char *caps = irc_line_to_string_skip(line, 2);
if (caps) {
caps++;
WRITE_LINE3(CONN(ic), NULL, "CAP", "*", "NAK", caps);
}
}
if ((ic->state & IRCC_READY) == IRCC_READY)
return irc_cli_startup(bip, ic, line);
return OK_FORGET;
}
static int irc_cli_quit(struct link_client *ic, struct line *line) static int irc_cli_quit(struct link_client *ic, struct line *line)
{ {
(void)ic; (void)ic;
@ -1403,6 +1433,8 @@ static int irc_dispatch_logging_client(bip_t *bip, struct link_client *ic,
return irc_cli_user(bip, ic, line); return irc_cli_user(bip, ic, line);
} else if (irc_line_elem_equals(line, 0, "PASS")) { } else if (irc_line_elem_equals(line, 0, "PASS")) {
return irc_cli_pass(bip, ic, line); return irc_cli_pass(bip, ic, line);
} else if (irc_line_elem_equals(line, 0, "CAP")) {
return irc_cli_cap(bip, ic, line);
} }
return OK_FORGET; return OK_FORGET;
} }
@ -1448,6 +1480,17 @@ static int origin_is_me(struct line *l, struct link_server *server)
return 0; return 0;
} }
static int irc_generic_error(struct link_server *server, struct line *line)
{
if (irc_line_count(line) == 4)
mylog(LOG_INFO, "[%s] IRC error: %s", LINK(server)->name,
irc_line_elem(line, 3));
else
mylog(LOG_INFO, "[%s] IRC error: %s", LINK(server)->name,
irc_line_to_string(line));
return OK_COPY;
}
static int irc_join(struct link_server *server, struct line *line) static int irc_join(struct link_server *server, struct line *line)
{ {
char *s_nick; char *s_nick;
@ -2166,15 +2209,16 @@ static int irc_server_sasl_authenticate(struct link_server *ircs)
size_t p_len = strlen(sasl_password); size_t p_len = strlen(sasl_password);
size_t raw_len = u_len * 2 + p_len + 2; size_t raw_len = u_len * 2 + p_len + 2;
size_t enc_len; size_t enc_len;
unsigned char *raw_str = bip_malloc(raw_len + 1); char *raw_str = bip_malloc(raw_len + 1);
unsigned char *enc_str; char *enc_str;
memcpy(raw_str, sasl_username, u_len); memcpy(raw_str, sasl_username, u_len);
raw_str[u_len] = '\0'; raw_str[u_len] = '\0';
memcpy(raw_str + u_len + 1, sasl_username, u_len); memcpy(raw_str + u_len + 1, sasl_username, u_len);
raw_str[u_len * 2 + 1] = '\0'; raw_str[u_len * 2 + 1] = '\0';
memcpy(raw_str + u_len * 2 + 2, sasl_password, p_len); memcpy(raw_str + u_len * 2 + 2, sasl_password, p_len);
enc_str = base64_encode(raw_str, raw_len, &enc_len); enc_str = base64_encode_no_lf(raw_str, raw_len, &enc_len);
free(raw_str);
mylog(LOG_DEBUG, "[%s] Base64 encoded SASL auth token (len %d): %s", mylog(LOG_DEBUG, "[%s] Base64 encoded SASL auth token (len %d): %s",
LINK(ircs)->name, enc_len, enc_str); LINK(ircs)->name, enc_len, enc_str);

View File

@ -90,6 +90,7 @@ list_t *parse_conf(FILE *file, int *err)
"log_level" { return LEX_LOG_LEVEL; } "log_level" { return LEX_LOG_LEVEL; }
"log_root" { return LEX_LOG_ROOT; } "log_root" { return LEX_LOG_ROOT; }
"log_format" { return LEX_LOG_FORMAT; } "log_format" { return LEX_LOG_FORMAT; }
"timestamp_format" { return LEX_TIMESTAMP_FORMAT; }
"backlog_lines" { return LEX_BACKLOG_LINES; } "backlog_lines" { return LEX_BACKLOG_LINES; }
"backlog_timestamp" { return LEX_BACKLOG_TIMESTAMP; } "backlog_timestamp" { return LEX_BACKLOG_TIMESTAMP; }
"backlog_no_timestamp" { return LEX_BACKLOG_NO_TIMESTAMP; } "backlog_no_timestamp" { return LEX_BACKLOG_NO_TIMESTAMP; }

View File

@ -16,6 +16,8 @@
#include "line.h" #include "line.h"
#include "util.h" #include "util.h"
char *_irc_line_to_string(struct line *l, int skip_first);
// TODO resolve assuming signed overflow does not occur when changing X +- C1 // TODO resolve assuming signed overflow does not occur when changing X +- C1
// cmp C2 to X cmp C2 -+ C1 // cmp C2 to X cmp C2 -+ C1
#pragma GCC diagnostic ignored "-Wstrict-overflow" #pragma GCC diagnostic ignored "-Wstrict-overflow"
@ -76,6 +78,19 @@ void irc_line_append(struct line *l, const char *s)
} }
char *irc_line_to_string(struct line *l) char *irc_line_to_string(struct line *l)
{
return _irc_line_to_string(l, 0);
}
char *irc_line_to_string_skip(struct line *l, int skip_first)
{
if (skip_first >= irc_line_count(l)) {
return NULL;
}
return _irc_line_to_string(l, skip_first);
}
char *_irc_line_to_string(struct line *l, int skip_first)
{ {
size_t len = 0; size_t len = 0;
int i; int i;
@ -83,7 +98,7 @@ char *irc_line_to_string(struct line *l)
if (l->origin) if (l->origin)
len = strlen(l->origin) + 2; len = strlen(l->origin) + 2;
for (i = 0; i < array_count(&l->words); i++) for (i = skip_first; i < array_count(&l->words); i++)
len += strlen(array_get(&l->words, i)) + 1; len += strlen(array_get(&l->words, i)) + 1;
len += 1; /* remove one trailing space and add \r\n */ len += 1; /* remove one trailing space and add \r\n */
len++; /* last args ":" */ len++; /* last args ":" */
@ -95,7 +110,7 @@ char *irc_line_to_string(struct line *l)
strcat(ret, l->origin); strcat(ret, l->origin);
strcat(ret, " "); strcat(ret, " ");
} }
for (i = 0; i < array_count(&l->words) - 1; i++) { for (i = skip_first; i < array_count(&l->words) - 1; i++) {
strcat(ret, array_get(&l->words, i)); strcat(ret, array_get(&l->words, i));
strcat(ret, " "); strcat(ret, " ");
} }
@ -143,6 +158,13 @@ void irc_line_drop(struct line *line, int elem)
bip_cfree(array_drop(&line->words, elem)); bip_cfree(array_drop(&line->words, elem));
} }
unsigned int irc_line_is_error(struct line *line)
{
const char *irc_code = irc_line_elem(line, 0);
const char *error_code = "4";
return (irc_code[0] == error_code[0]);
}
int irc_line_elem_equals(struct line *line, int elem, const char *cmp) int irc_line_elem_equals(struct line *line, int elem, const char *cmp)
{ {
return !strcmp(irc_line_elem(line, elem), cmp); return !strcmp(irc_line_elem(line, elem), cmp);

View File

@ -90,6 +90,7 @@ void irc_line_write(struct line *l, connection_t *c);
void irc_line_append(struct line *l, const char *s); void irc_line_append(struct line *l, const char *s);
struct line *irc_line_new_from_string(char *str); struct line *irc_line_new_from_string(char *str);
char *irc_line_to_string(struct line *l); char *irc_line_to_string(struct line *l);
char *irc_line_to_string_skip(struct line *l, int skip_first);
char *irc_line_to_string_to(struct line *line, char *nick); char *irc_line_to_string_to(struct line *line, char *nick);
void irc_line_free(struct line *l); void irc_line_free(struct line *l);
struct line *irc_line_dup(struct line *line); struct line *irc_line_dup(struct line *line);
@ -98,6 +99,7 @@ int irc_line_includes(struct line *line, int elem);
const char *irc_line_elem(struct line *line, int elem); const char *irc_line_elem(struct line *line, int elem);
int irc_line_count(struct line *line); int irc_line_count(struct line *line);
char *irc_line_pop(struct line *l); char *irc_line_pop(struct line *l);
unsigned int irc_line_is_error(struct line *line);
int irc_line_elem_equals(struct line *line, int elem, const char *cmp); int irc_line_elem_equals(struct line *line, int elem, const char *cmp);
int irc_line_elem_case_equals(struct line *line, int elem, const char *cmp); int irc_line_elem_case_equals(struct line *line, int elem, const char *cmp);
void irc_line_drop(struct line *line, int elem); void irc_line_drop(struct line *line, int elem);

View File

@ -27,6 +27,7 @@ extern int errno;
extern int log_level; extern int log_level;
extern char *conf_log_root; extern char *conf_log_root;
extern char *conf_log_format; extern char *conf_log_format;
extern char *conf_timestamp_format;
extern int conf_log; extern int conf_log;
extern FILE *conf_global_log_file; extern FILE *conf_global_log_file;
@ -1030,7 +1031,7 @@ static time_t compute_time(const char *buf)
time(&tv); time(&tv);
tm = *localtime(&tv); tm = *localtime(&tv);
if (strptime(buf, "%d-%m-%Y %H:%M:%S", &tm) == NULL) if (strptime(buf, conf_timestamp_format, &tm) == NULL)
return (time_t)-1; return (time_t)-1;
return mktime(&tm); return mktime(&tm);
} }

View File

@ -29,11 +29,13 @@
extern int conf_log_level; extern int conf_log_level;
extern int conf_log_system; extern int conf_log_system;
extern char *conf_timestamp_format;
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-prototypes" #pragma GCC diagnostic ignored "-Wstrict-prototypes"
extern int errno; extern int errno;
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
extern FILE *conf_global_log_file; extern FILE *conf_global_log_file;
void _mylog(int level, int with_timestamp, char *fmt, va_list ap);
void memory_fatal(void) void memory_fatal(void)
{ {
@ -222,27 +224,46 @@ int is_valid_username(char *str)
char *timestamp(void) char *timestamp(void)
{ {
static char ts[20];
time_t tv; time_t tv;
struct tm *tm; struct tm *tm;
time(&tv); time(&tv);
tm = localtime(&tv); tm = localtime(&tv);
strftime(ts, (size_t)20, "%d-%m-%Y %H:%M:%S", tm); return bip_strftime(tm);
return ts;
} }
char *hrtime(time_t s) char *hrtime(time_t s)
{ {
static char ts[20];
struct tm *tm; struct tm *tm;
if (s == 0) if (s == 0)
return "never"; return "never";
tm = localtime(&s); tm = localtime(&s);
strftime(ts, (size_t)20, "%d-%m-%Y %H:%M:%S", tm); return bip_strftime(tm);
}
char *bip_strftime(struct tm *tm)
{
static char ts[TIMESTAMP_FORMAT_MAXLEN];
size_t len, written;
ts[0] = '\0';
len = strlen(conf_timestamp_format);
if (len > TIMESTAMP_FORMAT_MAXLEN)
fatal_nots("Timestamp format itself exceeds "
"maximum allowed length(%d)", TIMESTAMP_FORMAT_MAXLEN);
/* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39438 */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
written = strftime(ts, (size_t)TIMESTAMP_FORMAT_MAXLEN, conf_timestamp_format, tm);
#pragma GCC diagnostic pop
/* if format len is 0, format is empty and written will also return 0 */
if (!written && !errno && len != 0)
mylog_nots(LOG_WARN, "Truncated timestamp, result exceeds "
"maximum size of %d characters.", TIMESTAMP_FORMAT_MAXLEN);
return ts; return ts;
} }
@ -260,7 +281,7 @@ char *checkmode2text(int v)
} }
#endif #endif
void _mylog(int level, char *fmt, va_list ap) void _mylog(int level, int with_timestamp, char *fmt, va_list ap)
{ {
char *prefix; char *prefix;
@ -294,7 +315,11 @@ void _mylog(int level, char *fmt, va_list ap)
break; break;
} }
fprintf(conf_global_log_file, "%s %s", timestamp(), prefix); if (with_timestamp)
fprintf(conf_global_log_file, "%s %s", timestamp(), prefix);
else
fprintf(conf_global_log_file, "%s", prefix);
vfprintf(conf_global_log_file, fmt, ap); vfprintf(conf_global_log_file, fmt, ap);
fprintf(conf_global_log_file, "\n"); fprintf(conf_global_log_file, "\n");
#ifdef DEBUG #ifdef DEBUG
@ -307,7 +332,16 @@ void mylog(int level, char *fmt, ...)
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
_mylog(level, fmt, ap); _mylog(level, 1, fmt, ap);
va_end(ap);
}
void mylog_nots(int level, char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_mylog(level, 0, fmt, ap);
va_end(ap); va_end(ap);
} }
@ -330,7 +364,22 @@ void fatal(char *fmt, ...)
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
_mylog(LOG_FATAL, fmt, ap); _mylog(LOG_FATAL, 1, fmt, ap);
va_end(ap);
#ifdef HAVE_BACKTRACE
dump_trace();
#endif
exit(200);
}
void fatal_nots(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_mylog(LOG_FATAL, 0, fmt, ap);
va_end(ap); va_end(ap);
#ifdef HAVE_BACKTRACE #ifdef HAVE_BACKTRACE

View File

@ -31,10 +31,14 @@
#define HASH_NOCASE 1 #define HASH_NOCASE 1
#define HASH_DEFAULT 0 #define HASH_DEFAULT 0
#define TIMESTAMP_FORMAT_MAXLEN 50
void mylog(int level, char *fmt, ...); void mylog(int level, char *fmt, ...);
void _mylog(int level, char *fmt, va_list ap); void mylog_nots(int level, char *fmt, ...);
void fatal(char *fmt, ...); void fatal(char *fmt, ...);
void fatal_nots(char *fmt, ...);
char *timestamp(void); char *timestamp(void);
char *bip_strftime(struct tm *tm);
struct hash_item; struct hash_item;
struct list_item { struct list_item {

104
src/utils/b64/base64.c Normal file
View File

@ -0,0 +1,104 @@
/*
* Base64 encoding/decoding (RFC1341)
* Copyright (c) 2005-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include <stdint.h>
#include "utils/common.h"
#include "os.h"
#include "base64.h"
static const char base64_table[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
#define BASE64_PAD BIT(0)
#define BASE64_LF BIT(1)
static char * base64_gen_encode(const unsigned char *src, size_t len,
size_t *out_len, const char *table, int add_pad)
{
char *out, *pos;
const unsigned char *end, *in;
size_t olen;
int line_len;
if (len >= SIZE_MAX / 4)
return NULL;
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
if (add_pad & BASE64_LF)
olen += olen / 72; /* line feeds */
olen++; /* nul termination */
if (olen < len)
return NULL; /* integer overflow */
out = os_malloc(olen);
if (out == NULL)
return NULL;
end = src + len;
in = src;
pos = out;
line_len = 0;
while (end - in >= 3) {
*pos++ = table[(in[0] >> 2) & 0x3f];
*pos++ = table[(((in[0] & 0x03) << 4) | (in[1] >> 4)) & 0x3f];
*pos++ = table[(((in[1] & 0x0f) << 2) | (in[2] >> 6)) & 0x3f];
*pos++ = table[in[2] & 0x3f];
in += 3;
line_len += 4;
if ((add_pad & BASE64_LF) && line_len >= 72) {
*pos++ = '\n';
line_len = 0;
}
}
if (end - in) {
*pos++ = table[(in[0] >> 2) & 0x3f];
if (end - in == 1) {
*pos++ = table[((in[0] & 0x03) << 4) & 0x3f];
if (add_pad & BASE64_PAD)
*pos++ = '=';
} else {
*pos++ = table[(((in[0] & 0x03) << 4) |
(in[1] >> 4)) & 0x3f];
*pos++ = table[((in[1] & 0x0f) << 2) & 0x3f];
}
if (add_pad & BASE64_PAD)
*pos++ = '=';
line_len += 4;
}
if ((add_pad & BASE64_LF) && line_len)
*pos++ = '\n';
*pos = '\0';
if (out_len)
*out_len = (size_t)(pos - out);
return out;
}
/**
* base64_encode - Base64 encode
* @src: Data to be encoded
* @len: Length of the data to be encoded
* @out_len: Pointer to output length variable, or %NULL if not used
* Returns: Allocated buffer of out_len bytes of encoded data,
* or %NULL on failure
*
* Caller is responsible for freeing the returned buffer. Returned buffer is
* nul terminated to make it easier to use as a C string. The nul terminator is
* not included in out_len.
*/
char * base64_encode_no_lf(const void *src, size_t len, size_t *out_len)
{
return base64_gen_encode(src, len, out_len, base64_table, BASE64_PAD);
}

View File

@ -1,7 +1,6 @@
/* /*
* Base64 encoding/decoding (RFC1341) * Base64 encoding/decoding (RFC1341)
* Copyright (c) 2005, Jouni Malinen <j@w1.fi> * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
* Copyright (c) 2022 Loïc Gomez
* *
* This software may be distributed under the terms of the BSD license. * This software may be distributed under the terms of the BSD license.
* See README for more details. * See README for more details.
@ -10,7 +9,7 @@
#ifndef BASE64_H #ifndef BASE64_H
#define BASE64_H #define BASE64_H
unsigned char *base64_encode(const unsigned char *src, size_t len, char * base64_encode_no_lf(const void *src, size_t len, size_t *out_len);
size_t *out_len);
#endif /* BASE64_H */ #endif /* BASE64_H */

4
src/utils/b64/includes.h Normal file
View File

@ -0,0 +1,4 @@
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

3
src/utils/b64/os.h Normal file
View File

@ -0,0 +1,3 @@
#ifndef os_malloc
#define os_malloc(s) malloc((s))
#endif

View File

@ -0,0 +1,3 @@
#ifndef BIT
#define BIT(x) (int)(1U << (x))
#endif

View File

@ -1,93 +0,0 @@
/*
* Base64 encoding/decoding (RFC1341)
* Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
* Copyright (c) 2022 Loïc Gomez
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "base64.h"
static const unsigned char base64_table[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**
* base64_encode - Base64 encode
* @src: Data to be encoded
* @len: Length of the data to be encoded
* @out_len: Pointer to output length variable, or %NULL if not used
* Returns: Allocated buffer of out_len bytes of encoded data,
* or %NULL on failure
*
* Caller is responsible for freeing the returned buffer. Returned buffer is
* nul terminated to make it easier to use as a C string. The nul terminator is
* not included in out_len.
*
* BIP change: remove line returns.
*/
unsigned char *base64_encode(const unsigned char *src, size_t len,
size_t *out_len)
{
unsigned char *out, *pos;
const unsigned char *end, *in;
size_t olen;
int line_len;
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
olen += olen / 72; /* line feeds */
olen++; /* nul termination */
if (olen < len)
return NULL; /* integer overflow */
out = malloc(olen);
if (out == NULL)
return NULL;
end = src + len;
in = src;
pos = out;
line_len = 0;
while (end - in >= 3) {
*pos++ = base64_table[in[0] >> 2];
*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
*pos++ = base64_table[in[2] & 0x3f];
in += 3;
line_len += 4;
/*
* BIP change: remove line returns.
if (line_len >= 72) {
*pos++ = '\n';
line_len = 0;
}
*/
}
if (end - in) {
*pos++ = base64_table[in[0] >> 2];
if (end - in == 1) {
*pos++ = base64_table[(in[0] & 0x03) << 4];
*pos++ = '=';
} else {
*pos++ = base64_table[((in[0] & 0x03) << 4)
| (in[1] >> 4)];
*pos++ = base64_table[(in[1] & 0x0f) << 2];
}
*pos++ = '=';
line_len += 4;
}
/*
* BIP change: remove line returns.
if (line_len)
*pos++ = '\n';
*/
*pos = '\0';
if (out_len)
*out_len = (size_t)(pos - out);
return out;
}