1 /**
2  * Copyright (c) 2013, Timothy Stack
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * * Redistributions of source code must retain the above copyright notice, this
10  * list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  * * Neither the name of Timothy Stack nor the names of its contributors
15  * may be used to endorse or promote products derived from this software
16  * without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  * @file logfile_sub_source.hh
30  */
31 
32 #include "config.h"
33 
34 #include <string.h>
35 #include <sqlite3.h>
36 
37 #include <arpa/inet.h>
38 #include <netinet/in.h>
39 #include <sys/socket.h>
40 
41 #include "log_level.hh"
42 #include "base/strnatcmp.h"
43 
44 #define MAX_ADDR_LEN    128
45 
try_inet_pton(int p_len,const char * p,char * n)46 static int try_inet_pton(int p_len, const char *p, char *n)
47 {
48     static int ADDR_FAMILIES[] = { AF_INET, AF_INET6 };
49 
50     char buf[MAX_ADDR_LEN + 1];
51     int  retval = AF_MAX;
52 
53     strncpy(buf, p, p_len);
54     buf[p_len] = '\0';
55     for (int family : ADDR_FAMILIES) {
56         if (inet_pton(family, buf, n) == 1) {
57             retval = family;
58             break;
59         }
60     }
61 
62     return retval;
63 }
64 
convert_v6_to_v4(int family,char * n)65 static int convert_v6_to_v4(int family, char *n)
66 {
67     struct in6_addr *ia = (struct in6_addr *)n;
68 
69     if (family == AF_INET6 &&
70         (IN6_IS_ADDR_V4COMPAT(ia) ||
71          IN6_IS_ADDR_V4MAPPED(ia))) {
72         family = AF_INET;
73         memmove(n, n + 12, sizeof(struct in_addr));
74     }
75 
76     return family;
77 }
78 
79 static
ipaddress(void * ptr,int a_len,const void * a_in,int b_len,const void * b_in)80 int ipaddress(void *ptr,
81               int a_len, const void *a_in,
82               int b_len, const void *b_in)
83 {
84     char a_addr[sizeof(struct in6_addr)],
85          b_addr[sizeof(struct in6_addr)];
86     const char *a_str = (const char *)a_in, *b_str = (const char *)b_in;
87     int         a_family, b_family, retval;
88 
89     if ((a_len > MAX_ADDR_LEN) || (b_len > MAX_ADDR_LEN)) {
90         return strnatcasecmp(a_len, a_str, b_len, b_str);
91     }
92 
93     int v4res = 0;
94     if (ipv4cmp(a_len, a_str, b_len, b_str, &v4res)) {
95         return v4res;
96     }
97 
98     a_family = try_inet_pton(a_len, a_str, a_addr);
99     b_family = try_inet_pton(b_len, b_str, b_addr);
100 
101     if (a_family == AF_MAX && b_family == AF_MAX) {
102         return strnatcasecmp(a_len, a_str, b_len, b_str);
103     }
104     else if (a_family == AF_MAX && b_family != AF_MAX) {
105         retval = -1;
106     }
107     else if (a_family != AF_MAX && b_family == AF_MAX) {
108         retval = 1;
109     }
110     else {
111         a_family = convert_v6_to_v4(a_family, a_addr);
112         b_family = convert_v6_to_v4(b_family, b_addr);
113         if (a_family == b_family) {
114             retval = memcmp(a_addr, b_addr,
115                             a_family == AF_INET ?
116                             sizeof(struct in_addr) :
117                             sizeof(struct in6_addr));
118         }
119         else if (a_family == AF_INET) {
120             retval = -1;
121         }
122         else {
123             retval = 1;
124         }
125     }
126 
127     return retval;
128 }
129 
130 static
sql_strnatcmp(void * ptr,int a_len,const void * a_in,int b_len,const void * b_in)131 int sql_strnatcmp(void *ptr,
132                   int a_len, const void *a_in,
133                   int b_len, const void *b_in)
134 {
135     return strnatcmp(a_len, (char *)a_in, b_len, (char *)b_in);
136 }
137 
138 static
sql_strnatcasecmp(void * ptr,int a_len,const void * a_in,int b_len,const void * b_in)139 int sql_strnatcasecmp(void *ptr,
140                       int a_len, const void *a_in,
141                       int b_len, const void *b_in)
142 {
143     return strnatcasecmp(a_len, (char *)a_in, b_len, (char *)b_in);
144 }
145 
146 static
sql_loglevelcmp(void * ptr,int a_len,const void * a_in,int b_len,const void * b_in)147 int sql_loglevelcmp(void *ptr,
148     int a_len, const void *a_in,
149     int b_len, const void *b_in)
150 {
151     return levelcmp((const char *)a_in, a_len,
152         (const char *)b_in, b_len);
153 }
154 
register_collation_functions(sqlite3 * db)155 int register_collation_functions(sqlite3 *db)
156 {
157     sqlite3_create_collation(db, "ipaddress", SQLITE_UTF8, nullptr, ipaddress);
158     sqlite3_create_collation(db, "naturalcase", SQLITE_UTF8, nullptr,
159                              sql_strnatcmp);
160     sqlite3_create_collation(db, "naturalnocase", SQLITE_UTF8, nullptr,
161                              sql_strnatcasecmp);
162     sqlite3_create_collation(db, "loglevel", SQLITE_UTF8, nullptr,
163                              sql_loglevelcmp);
164 
165     return 0;
166 }
167