630a4d5c8c7d414ba46a708d766d31e72db58a5b
[people/ms/libloc.git] / src / libloc.c
1 /*
2         libloc - A library to determine the location of someone on the Internet
3
4         Copyright (C) 2017 IPFire Development Team <info@ipfire.org>
5
6         This library is free software; you can redistribute it and/or
7         modify it under the terms of the GNU Lesser General Public
8         License as published by the Free Software Foundation; either
9         version 2.1 of the License, or (at your option) any later version.
10
11         This library is distributed in the hope that it will be useful,
12         but WITHOUT ANY WARRANTY; without even the implied warranty of
13         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14         Lesser General Public License for more details.
15 */
16
17 #include <arpa/inet.h>
18 #include <netinet/in.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stddef.h>
22 #include <stdarg.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <ctype.h>
27
28 #include <loc/libloc.h>
29 #include <loc/database.h>
30 #include <loc/private.h>
31
32 struct loc_ctx {
33         int refcount;
34         void (*log_fn)(struct loc_ctx* ctx,
35                 int priority, const char *file, int line, const char *fn,
36                 const char *format, va_list args);
37         int log_priority;
38
39         struct loc_database* db;
40 };
41
42 void loc_log(struct loc_ctx* ctx,
43                 int priority, const char* file, int line, const char* fn,
44                 const char* format, ...) {
45         va_list args;
46
47         va_start(args, format);
48         ctx->log_fn(ctx, priority, file, line, fn, format, args);
49         va_end(args);
50 }
51
52 static void log_stderr(struct loc_ctx* ctx,
53                 int priority, const char* file, int line, const char* fn,
54                 const char* format, va_list args) {
55         fprintf(stderr, "libloc: %s: ", fn);
56         vfprintf(stderr, format, args);
57 }
58
59 static int log_priority(const char* priority) {
60         char *endptr;
61
62         int prio = strtol(priority, &endptr, 10);
63
64         if (endptr[0] == '\0' || isspace(endptr[0]))
65                 return prio;
66
67         if (strncmp(priority, "err", 3) == 0)
68                 return LOG_ERR;
69
70         if (strncmp(priority, "info", 4) == 0)
71                 return LOG_INFO;
72
73         if (strncmp(priority, "debug", 5) == 0)
74                 return LOG_DEBUG;
75
76         return 0;
77 }
78
79 LOC_EXPORT int loc_new(struct loc_ctx** ctx) {
80         struct loc_ctx* c = calloc(1, sizeof(*c));
81         if (!c)
82                 return -ENOMEM;
83
84         c->refcount = 1;
85         c->log_fn = log_stderr;
86         c->log_priority = LOG_ERR;
87
88         c->db = NULL;
89
90         const char* env = secure_getenv("LOC_LOG");
91         if (env)
92                 loc_set_log_priority(c, log_priority(env));
93
94         INFO(c, "ctx %p created\n", c);
95         DEBUG(c, "log_priority=%d\n", c->log_priority);
96         *ctx = c;
97
98         return 0;
99 }
100
101 LOC_EXPORT struct loc_ctx* loc_ref(struct loc_ctx* ctx) {
102         if (!ctx)
103                 return NULL;
104
105         ctx->refcount++;
106
107         return ctx;
108 }
109
110 LOC_EXPORT struct loc_ctx* loc_unref(struct loc_ctx* ctx) {
111         if (!ctx)
112                 return NULL;
113
114         if (--ctx->refcount > 0)
115                 return NULL;
116
117         // Release any loaded databases
118         if (ctx->db)
119                 loc_database_unref(ctx->db);
120
121         INFO(ctx, "context %p released\n", ctx);
122         free(ctx);
123
124         return NULL;
125 }
126
127 LOC_EXPORT void loc_set_log_fn(struct loc_ctx* ctx,
128                 void (*log_fn)(struct loc_ctx* ctx, int priority, const char* file,
129                 int line, const char* fn, const char* format, va_list args)) {
130         ctx->log_fn = log_fn;
131         INFO(ctx, "custom logging function %p registered\n", log_fn);
132 }
133
134 LOC_EXPORT int loc_get_log_priority(struct loc_ctx* ctx) {
135         return ctx->log_priority;
136 }
137
138 LOC_EXPORT void loc_set_log_priority(struct loc_ctx* ctx, int priority) {
139         ctx->log_priority = priority;
140 }
141
142 LOC_EXPORT int loc_load(struct loc_ctx* ctx, const char* path) {
143         FILE* f = fopen(path, "r");
144         if (!f)
145                 return -errno;
146
147         // Release any previously openend database
148         if (ctx->db)
149                 loc_database_unref(ctx->db);
150
151         // Open the new database
152         int r = loc_database_new(ctx, &ctx->db, f);
153         if (r)
154                 return r;
155
156         // Close the file
157         fclose(f);
158
159         return 0;
160 }
161
162 LOC_EXPORT int loc_parse_address(struct loc_ctx* ctx, const char* string, struct in6_addr* address) {
163         DEBUG(ctx, "Paring IP address %s\n", string);
164
165         // Try parsing this as an IPv6 address
166         int r = inet_pton(AF_INET6, string, address);
167
168         // If inet_pton returns one it has been successful
169         if (r == 1) {
170                 DEBUG(ctx, "%s is an IPv6 address\n", string);
171                 return 0;
172         }
173
174         // Try parsing this as an IPv4 address
175         struct in_addr ipv4_address;
176         r = inet_pton(AF_INET, string, &ipv4_address);
177         if (r == 1) {
178                 DEBUG(ctx, "%s is an IPv4 address\n", string);
179
180                 // Convert to IPv6-mapped address
181                 address->s6_addr32[0] = htonl(0x0000);
182                 address->s6_addr32[1] = htonl(0x0000);
183                 address->s6_addr32[2] = htonl(0xffff);
184                 address->s6_addr32[3] = ipv4_address.s_addr;
185
186                 return 0;
187         }
188
189         DEBUG(ctx, "%s is not an valid IP address\n", string);
190         return 1;
191 }