671 lines
19 KiB
C
671 lines
19 KiB
C
|
|
|
|
#include <lighttpd/base.h>
|
|
#include <lighttpd/plugin_core.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
|
|
void fatal(const gchar* msg) {
|
|
fprintf(stderr, "%s\n", msg);
|
|
abort();
|
|
}
|
|
|
|
void fd_init(int fd) {
|
|
#ifdef _WIN32
|
|
int i = 1;
|
|
#endif
|
|
#ifdef FD_CLOEXEC
|
|
/* close fd on exec (cgi) */
|
|
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
|
#endif
|
|
#ifdef O_NONBLOCK
|
|
fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR);
|
|
#elif defined _WIN32
|
|
ioctlsocket(fd, FIONBIO, &i);
|
|
#endif
|
|
}
|
|
|
|
void ev_io_add_events(struct ev_loop *loop, ev_io *watcher, int events) {
|
|
if ((watcher->events & events) == events) return;
|
|
ev_io_stop(loop, watcher);
|
|
ev_io_set(watcher, watcher->fd, watcher->events | events);
|
|
ev_io_start(loop, watcher);
|
|
}
|
|
|
|
void ev_io_rem_events(struct ev_loop *loop, ev_io *watcher, int events) {
|
|
if (0 == (watcher->events & events)) return;
|
|
ev_io_stop(loop, watcher);
|
|
ev_io_set(watcher, watcher->fd, watcher->events & ~events);
|
|
ev_io_start(loop, watcher);
|
|
}
|
|
|
|
void ev_io_set_events(struct ev_loop *loop, ev_io *watcher, int events) {
|
|
if (events == (watcher->events & (EV_READ | EV_WRITE))) return;
|
|
ev_io_stop(loop, watcher);
|
|
ev_io_set(watcher, watcher->fd, (watcher->events & ~(EV_READ | EV_WRITE)) | events);
|
|
ev_io_start(loop, watcher);
|
|
}
|
|
|
|
|
|
/* converts hex char (0-9, A-Z, a-z) to decimal.
|
|
* returns -1 on invalid input.
|
|
*/
|
|
static int hex2int(unsigned char hex) {
|
|
int res;
|
|
if (hex >= 'A') { /* 'A' < 'a': hex >= 'A' --> hex >= 'a' */
|
|
if (hex >= 'a') {
|
|
res = hex - 'a' + 10;
|
|
} else {
|
|
res = hex - 'A' + 10;
|
|
}
|
|
} else {
|
|
res = hex - '0';
|
|
}
|
|
if (res > 15)
|
|
res = -1;
|
|
|
|
return res;
|
|
}
|
|
|
|
void url_decode(GString *path) {
|
|
char *src, *dst, c;
|
|
src = dst = path->str;
|
|
for ( ; *src; src++) {
|
|
c = *src;
|
|
if (c == '%') {
|
|
if (src[1] && src[2]) {
|
|
int a = hex2int(src[1]), b = hex2int(src[2]);
|
|
if (a != -1 && b != -1) {
|
|
c = (a << 4) | b;
|
|
if (c < 32 || c == 127) c = '_';
|
|
*(dst++) = c;
|
|
}
|
|
src += 2;
|
|
} else {
|
|
/* end of string */
|
|
return;
|
|
}
|
|
} else {
|
|
if (c < 32 || c == 127) c = '_';
|
|
*(dst++) = c;
|
|
}
|
|
}
|
|
g_string_set_size(path, dst - path->str);
|
|
}
|
|
|
|
/* Remove "/../", "//", "/./" parts from path.
|
|
*
|
|
* /blah/.. gets /
|
|
* /blah/../foo gets /foo
|
|
* /abc/./xyz gets /abc/xyz
|
|
* /abc//xyz gets /abc/xyz
|
|
*
|
|
* NOTE: src and dest can point to the same buffer, in which case
|
|
* the operation is performed in-place.
|
|
*/
|
|
|
|
void path_simplify(GString *path) {
|
|
int toklen;
|
|
char c, pre1;
|
|
char *start, *slash, *walk, *out;
|
|
unsigned short pre;
|
|
|
|
if (path == NULL)
|
|
return;
|
|
|
|
walk = start = out = slash = path->str;
|
|
while (*walk == ' ') {
|
|
walk++;
|
|
}
|
|
|
|
pre1 = *(walk++);
|
|
c = *(walk++);
|
|
pre = pre1;
|
|
if (pre1 != '/') {
|
|
pre = ('/' << 8) | pre1;
|
|
*(out++) = '/';
|
|
}
|
|
*(out++) = pre1;
|
|
|
|
if (pre1 == '\0') {
|
|
g_string_set_size(path, out - start);
|
|
return;
|
|
}
|
|
|
|
while (1) {
|
|
if (c == '/' || c == '\0') {
|
|
toklen = out - slash;
|
|
if (toklen == 3 && pre == (('.' << 8) | '.')) {
|
|
out = slash;
|
|
if (out > start) {
|
|
out--;
|
|
while (out > start && *out != '/') {
|
|
out--;
|
|
}
|
|
}
|
|
|
|
if (c == '\0')
|
|
out++;
|
|
} else if (toklen == 1 || pre == (('/' << 8) | '.')) {
|
|
out = slash;
|
|
if (c == '\0')
|
|
out++;
|
|
}
|
|
|
|
slash = out;
|
|
}
|
|
|
|
if (c == '\0')
|
|
break;
|
|
|
|
pre1 = c;
|
|
pre = (pre << 8) | pre1;
|
|
c = *walk;
|
|
*out = pre1;
|
|
|
|
out++;
|
|
walk++;
|
|
}
|
|
|
|
g_string_set_size(path, out - start);
|
|
}
|
|
|
|
#define SET_LEN_AND_RETURN_STR(x) \
|
|
do { \
|
|
*len = sizeof(x) - 1; \
|
|
return x; \
|
|
} while(0)
|
|
|
|
gchar *http_status_string(guint status_code, guint *len) {
|
|
/* RFC 2616 (as well as RFC 2518, RFC 2817, RFC 2295, RFC 2774, RFC 4918) */
|
|
switch (status_code) {
|
|
/* 1XX information */
|
|
case 100: SET_LEN_AND_RETURN_STR("Continue");
|
|
case 101: SET_LEN_AND_RETURN_STR("Switching Protocols");
|
|
case 102: SET_LEN_AND_RETURN_STR("Processing");
|
|
/* 2XX successful operation */
|
|
case 200: SET_LEN_AND_RETURN_STR("OK");
|
|
case 201: SET_LEN_AND_RETURN_STR("Created");
|
|
case 202: SET_LEN_AND_RETURN_STR("Accepted");
|
|
case 203: SET_LEN_AND_RETURN_STR("Non-Authoritative Information");
|
|
case 204: SET_LEN_AND_RETURN_STR("No Content");
|
|
case 205: SET_LEN_AND_RETURN_STR("Reset Content");
|
|
case 206: SET_LEN_AND_RETURN_STR("Partial Content");
|
|
case 207: SET_LEN_AND_RETURN_STR("Multi-Status");
|
|
/* 3XX redirect */
|
|
case 300: SET_LEN_AND_RETURN_STR("Multiple Choice");
|
|
case 301: SET_LEN_AND_RETURN_STR("Moved Permanently");
|
|
case 302: SET_LEN_AND_RETURN_STR("Found");
|
|
case 303: SET_LEN_AND_RETURN_STR("See Other");
|
|
case 304: SET_LEN_AND_RETURN_STR("Not Modified");
|
|
case 305: SET_LEN_AND_RETURN_STR("Use Proxy");
|
|
case 306: SET_LEN_AND_RETURN_STR("(reserved)");
|
|
case 307: SET_LEN_AND_RETURN_STR("Temporary Redirect");
|
|
/* 4XX client error */
|
|
case 400: SET_LEN_AND_RETURN_STR("Bad Request");
|
|
case 401: SET_LEN_AND_RETURN_STR("Unauthorized");
|
|
case 402: SET_LEN_AND_RETURN_STR("Payment Required");
|
|
case 403: SET_LEN_AND_RETURN_STR("Forbidden");
|
|
case 404: SET_LEN_AND_RETURN_STR("Not Found");
|
|
case 405: SET_LEN_AND_RETURN_STR("Method Not Allowed");
|
|
case 406: SET_LEN_AND_RETURN_STR("Not Acceptable");
|
|
case 407: SET_LEN_AND_RETURN_STR("Proxy Authentication Required");
|
|
case 408: SET_LEN_AND_RETURN_STR("Request Time-out");
|
|
case 409: SET_LEN_AND_RETURN_STR("Conflict");
|
|
case 410: SET_LEN_AND_RETURN_STR("Gone");
|
|
case 411: SET_LEN_AND_RETURN_STR("Length Required");
|
|
case 412: SET_LEN_AND_RETURN_STR("Precondition Failed");
|
|
case 413: SET_LEN_AND_RETURN_STR("Request Entity Too Large");
|
|
case 414: SET_LEN_AND_RETURN_STR("Request-URI Too Long");
|
|
case 415: SET_LEN_AND_RETURN_STR("Unsupported Media Type");
|
|
case 416: SET_LEN_AND_RETURN_STR("Request range not satisfiable");
|
|
case 417: SET_LEN_AND_RETURN_STR("Expectation Failed");
|
|
case 421: SET_LEN_AND_RETURN_STR("There are too many connections from your internet address");
|
|
case 422: SET_LEN_AND_RETURN_STR("Unprocessable Entity");
|
|
case 423: SET_LEN_AND_RETURN_STR("Locked");
|
|
case 424: SET_LEN_AND_RETURN_STR("Failed Dependency");
|
|
case 425: SET_LEN_AND_RETURN_STR("Unordered Collection");
|
|
case 426: SET_LEN_AND_RETURN_STR("Upgrade Required");
|
|
/* 5XX server error */
|
|
case 500: SET_LEN_AND_RETURN_STR("Internal Server Error");
|
|
case 501: SET_LEN_AND_RETURN_STR("Not Implemented");
|
|
case 502: SET_LEN_AND_RETURN_STR("Bad Gateway");
|
|
case 503: SET_LEN_AND_RETURN_STR("Service Unavailable");
|
|
case 504: SET_LEN_AND_RETURN_STR("Gateway Time-out");
|
|
case 505: SET_LEN_AND_RETURN_STR("HTTP Version not supported");
|
|
case 506: SET_LEN_AND_RETURN_STR("Variant Also Negotiates");
|
|
case 507: SET_LEN_AND_RETURN_STR("Insufficient Storage");
|
|
case 509: SET_LEN_AND_RETURN_STR("Bandwidth Limit Exceeded");
|
|
case 510: SET_LEN_AND_RETURN_STR("Not Extended");
|
|
|
|
/* unknown */
|
|
default: SET_LEN_AND_RETURN_STR("unknown status");
|
|
}
|
|
}
|
|
|
|
gchar *http_method_string(http_method_t method, guint *len) {
|
|
switch (method) {
|
|
case HTTP_METHOD_UNSET: SET_LEN_AND_RETURN_STR("UNKNOWN");
|
|
case HTTP_METHOD_GET: SET_LEN_AND_RETURN_STR("GET");
|
|
case HTTP_METHOD_POST: SET_LEN_AND_RETURN_STR("POST");
|
|
case HTTP_METHOD_HEAD: SET_LEN_AND_RETURN_STR("HEAD");
|
|
case HTTP_METHOD_OPTIONS: SET_LEN_AND_RETURN_STR("OPTIONS");
|
|
case HTTP_METHOD_PROPFIND: SET_LEN_AND_RETURN_STR("PROPFIND");
|
|
case HTTP_METHOD_MKCOL: SET_LEN_AND_RETURN_STR("MKCOL");
|
|
case HTTP_METHOD_PUT: SET_LEN_AND_RETURN_STR("PUT");
|
|
case HTTP_METHOD_DELETE: SET_LEN_AND_RETURN_STR("DELETE");
|
|
case HTTP_METHOD_COPY: SET_LEN_AND_RETURN_STR("COPY");
|
|
case HTTP_METHOD_MOVE: SET_LEN_AND_RETURN_STR("MOVE");
|
|
case HTTP_METHOD_PROPPATCH: SET_LEN_AND_RETURN_STR("PROPPATCH");
|
|
case HTTP_METHOD_REPORT: SET_LEN_AND_RETURN_STR("REPORT");
|
|
case HTTP_METHOD_CHECKOUT: SET_LEN_AND_RETURN_STR("CHECKOUT");
|
|
case HTTP_METHOD_CHECKIN: SET_LEN_AND_RETURN_STR("CHECKIN");
|
|
case HTTP_METHOD_VERSION_CONTROL: SET_LEN_AND_RETURN_STR("VERSION_CONTROL");
|
|
case HTTP_METHOD_UNCHECKOUT: SET_LEN_AND_RETURN_STR("UNCHECKOUT");
|
|
case HTTP_METHOD_MKACTIVITY: SET_LEN_AND_RETURN_STR("MKACTIVITY");
|
|
case HTTP_METHOD_MERGE: SET_LEN_AND_RETURN_STR("MERGE");
|
|
case HTTP_METHOD_LOCK: SET_LEN_AND_RETURN_STR("LOCK");
|
|
case HTTP_METHOD_UNLOCK: SET_LEN_AND_RETURN_STR("UNLOCK");
|
|
case HTTP_METHOD_LABEL: SET_LEN_AND_RETURN_STR("LABEL");
|
|
case HTTP_METHOD_CONNECT: SET_LEN_AND_RETURN_STR("CONNECT");
|
|
}
|
|
|
|
*len = 0;
|
|
return NULL;
|
|
}
|
|
|
|
gchar *http_version_string(http_version_t method, guint *len) {
|
|
switch (method) {
|
|
case HTTP_VERSION_1_1: SET_LEN_AND_RETURN_STR("HTTP/1.1");
|
|
case HTTP_VERSION_1_0: SET_LEN_AND_RETURN_STR("HTTP/1.0");
|
|
case HTTP_VERSION_UNSET: SET_LEN_AND_RETURN_STR("HTTP/??");
|
|
}
|
|
|
|
*len = 0;
|
|
return NULL;
|
|
}
|
|
|
|
#undef SET_LEN_AND_RETURN_STR
|
|
|
|
void http_status_to_str(gint status_code, gchar status_str[]) {
|
|
status_str[2] = status_code % 10 + '0';
|
|
status_code /= 10;
|
|
status_str[1] = status_code % 10 + '0';
|
|
status_code /= 10;
|
|
status_str[0] = status_code + '0';
|
|
}
|
|
|
|
|
|
gchar counter_format(guint64 *count, guint factor) {
|
|
gchar suffix = 0;
|
|
|
|
if (*count >= factor) { *count /= factor; suffix = 'k';
|
|
if (*count >= factor) { *count /= factor; suffix = 'm';
|
|
if (*count >= factor) { *count /= factor; suffix = 'g';
|
|
if (*count >= factor) { *count /= factor; suffix = 't';
|
|
if (*count >= factor) { *count /= factor; suffix = 'p';
|
|
if (*count >= factor) { *count /= factor; suffix = 'e'; }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return suffix;
|
|
}
|
|
|
|
GString *counter_format2(guint64 count, counter_type t, gint accuracy) {
|
|
GString *str = g_string_sized_new(64);
|
|
|
|
if (t == COUNTER_TIME) {
|
|
if (accuracy && count > (3600*24)) {
|
|
g_string_append_printf(str, "%" G_GUINT64_FORMAT " day%s", count / (3600*24), (count / (3600*24)) > 1 ? "s":"");
|
|
count %= 3600*24;
|
|
accuracy--;
|
|
}
|
|
if (accuracy && count > 3600) {
|
|
if (str->len)
|
|
g_string_append_printf(str, " %" G_GUINT64_FORMAT " hour%s", count / 3600, (count / 3600) > 1 ? "s":"");
|
|
else
|
|
g_string_append_printf(str, "%" G_GUINT64_FORMAT " hour%s", count / 3600, (count / 3600) > 1 ? "s":"");
|
|
count %= 3600;
|
|
accuracy--;
|
|
}
|
|
if (accuracy && count > 60) {
|
|
if (str->len)
|
|
g_string_append_printf(str, " %" G_GUINT64_FORMAT " min", count / 60);
|
|
else
|
|
g_string_append_printf(str, "%" G_GUINT64_FORMAT " min", count / 60);
|
|
count %= 60;
|
|
accuracy--;
|
|
}
|
|
if (accuracy && (count || !str->len)) {
|
|
if (str->len)
|
|
g_string_append_printf(str, " %" G_GUINT64_FORMAT " s", count);
|
|
else
|
|
g_string_append_printf(str, "%" G_GUINT64_FORMAT " s", count);
|
|
}
|
|
} else if (t == COUNTER_UNITS) {
|
|
if (accuracy && count > 1000000) {
|
|
g_string_append_printf(str, "%" G_GUINT64_FORMAT " m", count / 1000000);
|
|
count %= 1000000;
|
|
accuracy--;
|
|
}
|
|
if (accuracy && count > 1000) {
|
|
if (str->len)
|
|
g_string_append_printf(str, " %" G_GUINT64_FORMAT " k", count / 1000);
|
|
else
|
|
g_string_append_printf(str, "%" G_GUINT64_FORMAT " k", count / 1000);
|
|
count %= 1000;
|
|
accuracy--;
|
|
}
|
|
if (accuracy && (count || !str->len)) {
|
|
if (str->len)
|
|
g_string_append_printf(str, " %" G_GUINT64_FORMAT, count);
|
|
else
|
|
g_string_append_printf(str, "%" G_GUINT64_FORMAT, count);
|
|
}
|
|
} else if (t == COUNTER_BYTES) {
|
|
if (accuracy && count > (1024*1024*1024*G_GUINT64_CONSTANT(1024))) {
|
|
g_string_append_printf(str, "%" G_GUINT64_FORMAT " TiB", count / (1024*1024*1024*G_GUINT64_CONSTANT(1024)));
|
|
count %= (1024*1024*1024*G_GUINT64_CONSTANT(1024));
|
|
accuracy--;
|
|
}
|
|
if (accuracy && count > (1024*1024*1024)) {
|
|
if (str->len)
|
|
g_string_append_printf(str, " %" G_GUINT64_FORMAT " GiB", count / (1024*1024*1024));
|
|
else
|
|
g_string_append_printf(str, "%" G_GUINT64_FORMAT " GiB", count / (1024*1024*1024));
|
|
count %= (1024*1024*1024);
|
|
accuracy--;
|
|
}
|
|
if (accuracy && count > (1024*1024)) {
|
|
if (str->len)
|
|
g_string_append_printf(str, " %" G_GUINT64_FORMAT " MiB", count / (1024*1024));
|
|
else
|
|
g_string_append_printf(str, "%" G_GUINT64_FORMAT " MiB", count / (1024*1024));
|
|
count %= (1024*1024);
|
|
accuracy--;
|
|
}
|
|
if (accuracy && count > 1024) {
|
|
if (str->len)
|
|
g_string_append_printf(str, " %" G_GUINT64_FORMAT " KiB", count / 1024);
|
|
else
|
|
g_string_append_printf(str, "%" G_GUINT64_FORMAT " KiB", count / 1024);
|
|
count %= 1024;
|
|
accuracy--;
|
|
}
|
|
if (accuracy && (count || !str->len)) {
|
|
if (str->len)
|
|
g_string_append_printf(str, " %" G_GUINT64_FORMAT " B", count);
|
|
else
|
|
g_string_append_printf(str, "%" G_GUINT64_FORMAT " B", count);
|
|
}
|
|
} else if (t == COUNTER_BITS) {
|
|
if (accuracy && count > (1000*1000*1000)) {
|
|
if (str->len)
|
|
g_string_append_printf(str, " %" G_GUINT64_FORMAT " gb", count / (1000*1000*1000));
|
|
else
|
|
g_string_append_printf(str, "%" G_GUINT64_FORMAT " gbit", count / (1000*1000*1000));
|
|
count %= (1024*1024*1024);
|
|
accuracy--;
|
|
}
|
|
if (accuracy && count > (1024*1024)) {
|
|
if (str->len)
|
|
g_string_append_printf(str, " %" G_GUINT64_FORMAT " mbit", count / (1024*1024));
|
|
else
|
|
g_string_append_printf(str, "%" G_GUINT64_FORMAT " ", count / (1024*1024));
|
|
count %= (1024*1024);
|
|
accuracy--;
|
|
}
|
|
if (accuracy && count > 1024) {
|
|
if (str->len)
|
|
g_string_append_printf(str, " %" G_GUINT64_FORMAT " KiB", count / 1024);
|
|
else
|
|
g_string_append_printf(str, "%" G_GUINT64_FORMAT " KiB", count / 1024);
|
|
count %= 1024;
|
|
accuracy--;
|
|
}
|
|
if (accuracy && (count || !str->len)) {
|
|
if (str->len)
|
|
g_string_append_printf(str, " %" G_GUINT64_FORMAT " B", count);
|
|
else
|
|
g_string_append_printf(str, "%" G_GUINT64_FORMAT " B", count);
|
|
}
|
|
} else
|
|
g_string_append_len(str, CONST_STR_LEN("unknown counter type"));
|
|
|
|
return str;
|
|
}
|
|
|
|
|
|
gchar *ev_backend_string(guint backend) {
|
|
switch (backend) {
|
|
case EVBACKEND_SELECT: return "select";
|
|
case EVBACKEND_POLL: return "poll";
|
|
case EVBACKEND_EPOLL: return "epoll";
|
|
case EVBACKEND_KQUEUE: return "kqueue";
|
|
case EVBACKEND_DEVPOLL: return "devpoll";
|
|
case EVBACKEND_PORT: return "port";
|
|
default: return "unknown";
|
|
}
|
|
}
|
|
|
|
|
|
void string_destroy_notify(gpointer str) {
|
|
g_string_free((GString*)str, TRUE);
|
|
}
|
|
|
|
|
|
guint hash_ipv4(gconstpointer key) {
|
|
return *((guint*)key) * 2654435761;
|
|
}
|
|
|
|
guint hash_ipv6(gconstpointer key) {
|
|
guint *i = ((guint*)key);
|
|
return (i[0] ^ i[1] ^ i[2] ^ i[3]) * 2654435761;
|
|
}
|
|
|
|
|
|
GString *mimetype_get(vrequest *vr, GString *filename) {
|
|
/* search in mime_types option for the first match */
|
|
GArray *arr;
|
|
|
|
if (!vr || !filename || !filename->len)
|
|
return NULL;
|
|
|
|
arr = CORE_OPTION(CORE_OPTION_MIME_TYPES).list;
|
|
|
|
for (guint i = 0; i < arr->len; i++) {
|
|
gint k, j;
|
|
value *tuple = g_array_index(arr, value*, i);
|
|
GString *ext = g_array_index(tuple->data.list, value*, 0)->data.string;
|
|
if (ext->len > filename->len)
|
|
continue;
|
|
|
|
/* "" extension matches everything, used for default mimetype */
|
|
if (!ext->len)
|
|
return g_array_index(tuple->data.list, value*, 1)->data.string;
|
|
|
|
k = filename->len - 1;
|
|
j = ext->len - 1;
|
|
for (; j >= 0; j--) {
|
|
if (ext->str[j] != filename->str[k])
|
|
break;
|
|
k--;
|
|
}
|
|
|
|
if (j == -1)
|
|
return g_array_index(tuple->data.list, value*, 1)->data.string;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
GString *sockaddr_to_string(sock_addr *saddr, GString *dest, gboolean showport) {
|
|
gchar *p;
|
|
guint8 len = 0;
|
|
guint8 tmp;
|
|
guint8 tmplen;
|
|
guint8 oct;
|
|
|
|
switch (saddr->plain.sa_family) {
|
|
case AF_INET:
|
|
/* ipv4 */
|
|
if (!dest)
|
|
dest = g_string_sized_new(16+6);
|
|
else
|
|
g_string_set_size(dest, 16+6);
|
|
|
|
p = dest->str;
|
|
|
|
for (guint i = 0; i < 4; i++) {
|
|
oct = ((guint8*)&saddr->ipv4.sin_addr.s_addr)[i];
|
|
for (tmplen = 1, tmp = oct; tmp > 9; tmp /= 10)
|
|
tmplen++;
|
|
|
|
len += tmplen + 1;
|
|
tmp = tmplen;
|
|
|
|
p[tmplen] = '.';
|
|
|
|
for (p += tmplen-1; tmplen; tmplen--) {
|
|
*p = '0' + (oct % 10);
|
|
p--;
|
|
oct /= 10;
|
|
}
|
|
|
|
p += tmp + 2;
|
|
}
|
|
|
|
dest->str[len-1] = 0;
|
|
dest->len = len-1;
|
|
if (showport) g_string_append_printf(dest, ":%u", (unsigned int) ntohs(saddr->ipv4.sin_port));
|
|
break;
|
|
#ifdef HAVE_IPV6
|
|
case AF_INET6:
|
|
/* ipv6 - not yet implemented with own function */
|
|
if (!dest)
|
|
dest = g_string_sized_new(INET6_ADDRSTRLEN+6);
|
|
|
|
ipv6_tostring(dest, saddr->ipv6.sin6_addr.s6_addr);
|
|
if (showport) g_string_append_printf(dest, ":%u", (unsigned int) ntohs(saddr->ipv6.sin6_port));
|
|
#endif
|
|
#ifdef HAVE_SYS_UN_H
|
|
case AF_UNIX:
|
|
if (!dest)
|
|
dest = g_string_sized_new(0);
|
|
else
|
|
g_string_truncate(dest, 0);
|
|
g_string_append_len(dest, CONST_STR_LEN("unix:"));
|
|
g_string_append(dest, saddr->un.sun_path);
|
|
break;
|
|
#endif
|
|
default:
|
|
if (!dest)
|
|
dest = g_string_new_len(CONST_STR_LEN("unknown sockaddr family"));
|
|
else
|
|
l_g_string_assign_len(dest, CONST_STR_LEN("unknown sockaddr family"));
|
|
}
|
|
|
|
return dest;
|
|
}
|
|
|
|
sockaddr sockaddr_from_string(GString *str, guint tcp_default_port) {
|
|
guint32 ipv4;
|
|
#ifdef HAVE_IPV6
|
|
guint8 ipv6[16];
|
|
#endif
|
|
guint16 port = tcp_default_port;
|
|
sockaddr saddr = { 0, NULL };
|
|
|
|
#ifdef HAVE_SYS_UN_H
|
|
if (0 == strncmp(str->str, "unix:/", 6)) {
|
|
saddr.len = str->len + 1 - 5 + sizeof(saddr.addr->un.sun_family);
|
|
saddr.addr = (sock_addr*) g_slice_alloc0(saddr.len);
|
|
saddr.addr->un.sun_family = AF_UNIX;
|
|
strcpy(saddr.addr->un.sun_path, str->str + 5);
|
|
} else
|
|
#endif
|
|
if (parse_ipv4(str->str, &ipv4, NULL, &port)) {
|
|
if (!port) return saddr;
|
|
saddr.len = sizeof(struct sockaddr_in);
|
|
saddr.addr = (sock_addr*) g_slice_alloc0(saddr.len);
|
|
saddr.addr->ipv4.sin_family = AF_INET;
|
|
saddr.addr->ipv4.sin_addr.s_addr = ipv4;
|
|
saddr.addr->ipv4.sin_port = htons(port);
|
|
#ifdef HAVE_IPV6
|
|
} else
|
|
if (parse_ipv6(str->str, ipv6, NULL, &port)) {
|
|
if (!port) return saddr;
|
|
saddr.len = sizeof(struct sockaddr_in6);
|
|
saddr.addr = (sock_addr*) g_slice_alloc0(saddr.len);
|
|
saddr.addr->ipv6.sin6_family = AF_INET6;
|
|
memcpy(&saddr.addr->ipv6.sin6_addr, ipv6, 16);
|
|
saddr.addr->ipv6.sin6_port = htons(port);
|
|
#endif
|
|
}
|
|
return saddr;
|
|
}
|
|
|
|
void sockaddr_clear(sockaddr *saddr) {
|
|
g_slice_free1(saddr->len, saddr->addr);
|
|
}
|
|
|
|
/* unused */
|
|
void gstring_replace_char_with_str_len(GString *gstr, gchar c, gchar *str, guint len) {
|
|
for (guint i = 0; i < gstr->len; i++) {
|
|
if (gstr->str[i] == c) {
|
|
/* char found, replace */
|
|
gstr->str[i] = str[0];
|
|
if (len > 1)
|
|
g_string_insert_len(gstr, i, &str[1], len-1);
|
|
i += len - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
gboolean l_g_strncase_equal(GString *str, const gchar *s, guint len) {
|
|
if (str->len != len) return FALSE;
|
|
return 0 == g_ascii_strncasecmp(str->str, s, len);
|
|
}
|
|
|
|
GString *l_g_string_assign_len(GString *string, const gchar *val, gssize len) {
|
|
g_string_truncate(string, 0);
|
|
g_string_append_len(string, val, len);
|
|
return string;
|
|
}
|
|
|
|
/* http://womble.decadentplace.org.uk/readdir_r-advisory.html */
|
|
gsize dirent_buf_size(DIR * dirp) {
|
|
glong name_max;
|
|
gsize name_end;
|
|
|
|
# if !defined(HAVE_DIRFD)
|
|
UNUSED(dirp);
|
|
# endif
|
|
|
|
# if defined(HAVE_FPATHCONF) && defined(HAVE_DIRFD) && defined(_PC_NAME_MAX)
|
|
name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
|
|
if (name_max == -1)
|
|
# if defined(NAME_MAX)
|
|
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
|
|
# else
|
|
return (gsize)(-1);
|
|
# endif
|
|
# else
|
|
# if defined(NAME_MAX)
|
|
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
|
|
# else
|
|
# error "buffer size for readdir_r cannot be determined"
|
|
# endif
|
|
# endif
|
|
|
|
name_end = (gsize)offsetof(struct dirent, d_name) + name_max + 1;
|
|
|
|
return (name_end > sizeof(struct dirent) ? name_end : sizeof(struct dirent));
|
|
}
|