aboutsummaryrefslogtreecommitdiffstats
path: root/src/libserver/url.h
blob: 4ace18f1aa34075e1340d2b07ecab3911812a92f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
/* URL check functions */
#ifndef URL_H
#define URL_H

#include "config.h"
#include "mem_pool.h"
#include "khash.h"
#include "fstring.h"
#include "libutil/cxx/utf8_util.h"

#ifdef  __cplusplus
extern "C" {
#endif

struct rspamd_task;
struct rspamd_mime_text_part;

enum rspamd_url_flags {
	RSPAMD_URL_FLAG_PHISHED = 1u << 0u,
	RSPAMD_URL_FLAG_NUMERIC = 1u << 1u,
	RSPAMD_URL_FLAG_OBSCURED = 1u << 2u,
	RSPAMD_URL_FLAG_REDIRECTED = 1u << 3u,
	RSPAMD_URL_FLAG_HTML_DISPLAYED = 1u << 4u,
	RSPAMD_URL_FLAG_FROM_TEXT = 1u << 5u,
	RSPAMD_URL_FLAG_SUBJECT = 1u << 6u,
	RSPAMD_URL_FLAG_HOSTENCODED = 1u << 7u,
	RSPAMD_URL_FLAG_SCHEMAENCODED = 1u << 8u,
	RSPAMD_URL_FLAG_PATHENCODED = 1u << 9u,
	RSPAMD_URL_FLAG_QUERYENCODED = 1u << 10u,
	RSPAMD_URL_FLAG_MISSINGSLASHES = 1u << 11u,
	RSPAMD_URL_FLAG_IDN = 1u << 12u,
	RSPAMD_URL_FLAG_HAS_PORT = 1u << 13u,
	RSPAMD_URL_FLAG_HAS_USER = 1u << 14u,
	RSPAMD_URL_FLAG_SCHEMALESS = 1u << 15u,
	RSPAMD_URL_FLAG_UNNORMALISED = 1u << 16u,
	RSPAMD_URL_FLAG_ZW_SPACES = 1u << 17u,
	RSPAMD_URL_FLAG_DISPLAY_URL = 1u << 18u,
	RSPAMD_URL_FLAG_IMAGE = 1u << 19u,
	RSPAMD_URL_FLAG_QUERY = 1u << 20u,
	RSPAMD_URL_FLAG_CONTENT = 1u << 21u,
	RSPAMD_URL_FLAG_NO_TLD = 1u << 22u,
	RSPAMD_URL_FLAG_TRUNCATED = 1u << 23u,
	RSPAMD_URL_FLAG_REDIRECT_TARGET = 1u << 24u,
};
#define RSPAMD_URL_MAX_FLAG_SHIFT (25u)

struct rspamd_url_tag {
	const gchar *data;
	struct rspamd_url_tag *prev, *next;
};


struct rspamd_url {
	gchar *string;
	gchar *raw;

	gchar *visible_part;
	struct rspamd_url *linked_url;

	guint32 flags;

	guint8 protocol;
	guint8 protocollen;

	guint16 port;
	guint16 usershift;
	guint16 hostshift;
	guint16 datashift;
	guint16 queryshift;
	guint16 fragmentshift;
	guint16 tldshift;
	guint16 userlen;
	guint16 hostlen;
	guint16 datalen;
	guint16 querylen;
	guint16 fragmentlen;
	guint16 tldlen;
	guint16 count;
	guint16 urllen;
	guint16 rawlen;
};

#define rspamd_url_user(u) ((u)->userlen > 0 ? (u)->string + (u)->usershift : NULL)
#define rspamd_url_user_unsafe(u) ((u)->string + (u)->usershift)

#define rspamd_url_host(u) ((u)->hostlen > 0 ? (u)->string + (u)->hostshift : NULL)
#define rspamd_url_host_unsafe(u) ((u)->string + (u)->hostshift)
#define rspamd_url_tld_unsafe(u) ((u)->string + (u)->tldshift)

#define rspamd_url_data_unsafe(u) ((u)->string + (u)->datashift)
#define rspamd_url_query_unsafe(u) ((u)->string + (u)->queryshift)
#define rspamd_url_fragment_unsafe(u) ((u)->string + (u)->fragmentshift)

enum uri_errno {
	URI_ERRNO_OK = 0,           /* Parsing went well */
	URI_ERRNO_EMPTY,        /* The URI string was empty */
	URI_ERRNO_INVALID_PROTOCOL, /* No protocol was found */
	URI_ERRNO_INVALID_PORT,     /* Port number is bad */
	URI_ERRNO_BAD_ENCODING, /* Bad characters encoding */
	URI_ERRNO_BAD_FORMAT,
	URI_ERRNO_TLD_MISSING,
	URI_ERRNO_HOST_MISSING,
	URI_ERRNO_TOO_LONG,
};

enum rspamd_url_protocol {
	PROTOCOL_FILE = 1u << 0u,
	PROTOCOL_FTP = 1u << 1u,
	PROTOCOL_HTTP = 1u << 2u,
	PROTOCOL_HTTPS = 1u << 3u,
	PROTOCOL_MAILTO = 1u << 4u,
	PROTOCOL_TELEPHONE = 1u << 5u,
	PROTOCOL_UNKNOWN = 1u << 7u,
};

enum rspamd_url_parse_flags {
	RSPAMD_URL_PARSE_TEXT = 0u,
	RSPAMD_URL_PARSE_HREF = (1u << 0u),
	RSPAMD_URL_PARSE_CHECK = (1u << 1u),
};

enum rspamd_url_find_type {
	RSPAMD_URL_FIND_ALL = 0,
	RSPAMD_URL_FIND_STRICT,
};

/**
 * Initialize url library
 * @param cfg
 */
void rspamd_url_init(const gchar *tld_file);

void rspamd_url_deinit(void);

/*
 * Parse urls inside text
 * @param pool memory pool
 * @param task task object
 * @param part current text part
 * @param is_html turn on html euristic
 */
void rspamd_url_text_extract(rspamd_mempool_t *pool,
							 struct rspamd_task *task,
							 struct rspamd_mime_text_part *part,
							 enum rspamd_url_find_type how);

/*
 * Parse a single url into an uri structure
 * @param pool memory pool
 * @param uristring text form of url
 * @param uri url object, must be pre allocated
 */
enum uri_errno rspamd_url_parse(struct rspamd_url *uri,
								gchar *uristring,
								gsize len,
								rspamd_mempool_t *pool,
								enum rspamd_url_parse_flags flags);

/*
 * Try to extract url from a text
 * @param pool memory pool
 * @param begin begin of text
 * @param len length of text
 * @param start storage for start position of url found (or NULL)
 * @param end storage for end position of url found (or NULL)
 * @param url_str storage for url string(or NULL)
 * @return TRUE if url is found in specified text
 */
gboolean rspamd_url_find(rspamd_mempool_t *pool,
						 const gchar *begin, gsize len,
						 gchar **url_str,
						 enum rspamd_url_find_type how,
						 goffset *url_pos,
						 gboolean *prefix_added);

/*
 * Return text representation of url parsing error
 */
const gchar *rspamd_url_strerror(int err);


/**
 * Find TLD for a specified host string
 * @param in input host
 * @param inlen length of input
 * @param out output rspamd_ftok_t with tld position
 * @return TRUE if tld has been found
 */
gboolean rspamd_url_find_tld(const gchar *in, gsize inlen, rspamd_ftok_t *out);

typedef gboolean (*url_insert_function)(struct rspamd_url *url,
										gsize start_offset, gsize end_offset, void *ud);

/**
 * Search for multiple urls in text and call `func` for each url found
 * @param pool
 * @param in
 * @param inlen
 * @param is_html
 * @param func
 * @param ud
 */
void rspamd_url_find_multiple(rspamd_mempool_t *pool,
							  const gchar *in, gsize inlen,
							  enum rspamd_url_find_type how,
							  GPtrArray *nlines,
							  url_insert_function func,
							  gpointer ud);

/**
 * Search for a single url in text and call `func` for each url found
 * @param pool
 * @param in
 * @param inlen
 * @param is_html
 * @param func
 * @param ud
 */
void rspamd_url_find_single(rspamd_mempool_t *pool,
							const gchar *in, gsize inlen,
							enum rspamd_url_find_type how,
							url_insert_function func,
							gpointer ud);

/**
 * Generic callback to insert URLs into rspamd_task
 * @param url
 * @param start_offset
 * @param end_offset
 * @param ud
 */
gboolean rspamd_url_task_subject_callback(struct rspamd_url *url,
										  gsize start_offset,
										  gsize end_offset, gpointer ud);

/**
 * Decode URL encoded string in-place and return new length of a string, src and dst are NULL terminated
 * @param dst
 * @param src
 * @param size
 * @return
 */
gsize rspamd_url_decode(gchar *dst, const gchar *src, gsize size);

/**
 * Encode url if needed. In this case, memory is allocated from the specific pool.
 * Returns pointer to begin and encoded length in `dlen`
 * @param url
 * @param pool
 * @return
 */
const gchar *rspamd_url_encode(struct rspamd_url *url, gsize *dlen,
							   rspamd_mempool_t *pool);


/**
 * Returns if a character is domain character
 * @param c
 * @return
 */
gboolean rspamd_url_is_domain(int c);

/**
 * Returns symbolic name for protocol
 * @param proto
 * @return
 */
const gchar *rspamd_url_protocol_name(enum rspamd_url_protocol proto);


/**
 * Converts string to a numeric protocol
 * @param str
 * @return
 */
enum rspamd_url_protocol rspamd_url_protocol_from_string(const gchar *str);

/**
 * Converts string to a url flag
 * @param str
 * @param flag
 * @return
 */
bool rspamd_url_flag_from_string(const gchar *str, gint *flag);

/**
 * Converts url flag to a string
 * @param flag
 * @return
 */
const gchar *rspamd_url_flag_to_string(int flag);

/* Defines sets of urls indexed by url as is */
KHASH_DECLARE (rspamd_url_hash, struct rspamd_url *, char);
KHASH_DECLARE (rspamd_url_host_hash, struct rspamd_url *, char);

/* Convenience functions for url sets */
/**
 * Add an url to set or increase the existing url count
 * @param set
 * @param u
 * @return true if a new url has been added
 */
bool rspamd_url_set_add_or_increase(khash_t (rspamd_url_hash) *set,
									struct rspamd_url *u,
									bool enforce_replace);

/**
 * Same as rspamd_url_set_add_or_increase but returns the existing url if found
 * @param set
 * @param u
 * @return
 */
struct rspamd_url *rspamd_url_set_add_or_return(khash_t (rspamd_url_hash) *set,
												struct rspamd_url *u);
/**
 * Helper for url host set
 * @param set
 * @param u
 * @return
 */
bool rspamd_url_host_set_add(khash_t (rspamd_url_host_hash) *set,
							 struct rspamd_url *u);
/**
 * Checks if a url is in set
 * @param set
 * @param u
 * @return
 */
bool rspamd_url_set_has(khash_t (rspamd_url_hash) *set, struct rspamd_url *u);

bool rspamd_url_host_set_has(khash_t (rspamd_url_host_hash) *set, struct rspamd_url *u);

/**
 * Compares two urls (similar to C comparison functions) lexicographically
 * @param u1
 * @param u2
 * @return
 */
int rspamd_url_cmp(const struct rspamd_url *u1, const struct rspamd_url *u2);

/**
 * Same but used for qsort to sort `struct rspamd_url *[]` array
 * @param u1
 * @param u2
 * @return
 */
int rspamd_url_cmp_qsort(const void *u1, const void *u2);

/**
 * Normalize unicode input and set out url flags as appropriate
 * @param pool
 * @param input
 * @param len_out (must be &var)
 * @param url_flags_out (must be just a var with no dereference)
 */
#define rspamd_url_normalise_propagate_flags(pool, input, len_out, url_flags_out) \
  do {                                                                            \
     enum rspamd_normalise_result norm_res;                                       \
     norm_res = rspamd_normalise_unicode_inplace((input), (len_out));     \
     if (norm_res & RSPAMD_UNICODE_NORM_UNNORMAL) {                               \
       url_flags_out |= RSPAMD_URL_FLAG_UNNORMALISED;                             \
     }                                                                            \
     if (norm_res & RSPAMD_UNICODE_NORM_ZERO_SPACES) {                            \
       url_flags_out |= RSPAMD_URL_FLAG_ZW_SPACES;                                \
     }                                                                            \
     if (norm_res & (RSPAMD_UNICODE_NORM_ERROR)) {                                \
       url_flags_out |= RSPAMD_URL_FLAG_OBSCURED;                                 \
     }                                                                            \
  } while(0)
#ifdef  __cplusplus
}
#endif

#endif