]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/mac8.c
Post-PG 10 beta1 pgindent run
[postgresql] / src / backend / utils / adt / mac8.c
1 /*-------------------------------------------------------------------------
2  *
3  * mac8.c
4  *        PostgreSQL type definitions for 8 byte (EUI-64) MAC addresses.
5  *
6  * EUI-48 (6 byte) MAC addresses are accepted as input and are stored in
7  * EUI-64 format, with the 4th and 5th bytes set to FF and FE, respectively.
8  *
9  * Output is always in 8 byte (EUI-64) format.
10  *
11  * The following code is written with the assumption that the OUI field
12  * size is 24 bits.
13  *
14  * Portions Copyright (c) 1998-2017, PostgreSQL Global Development Group
15  *
16  * IDENTIFICATION
17  *                src/backend/utils/adt/mac8.c
18  *
19  *-------------------------------------------------------------------------
20  */
21
22 #include "postgres.h"
23
24 #include "access/hash.h"
25 #include "libpq/pqformat.h"
26 #include "utils/builtins.h"
27 #include "utils/inet.h"
28
29 /*
30  *      Utility macros used for sorting and comparing:
31  */
32 #define hibits(addr) \
33   ((unsigned long)(((addr)->a<<24) | ((addr)->b<<16) | ((addr)->c<<8) | ((addr)->d)))
34
35 #define lobits(addr) \
36   ((unsigned long)(((addr)->e<<24) | ((addr)->f<<16) | ((addr)->g<<8) | ((addr)->h)))
37
38 static unsigned char hex2_to_uchar(const unsigned char *str, const unsigned char *ptr);
39
40 static const signed char hexlookup[128] = {
41         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
42         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
43         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
44         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
45         -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
46         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
47         -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
48         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
49 };
50
51 /*
52  * hex2_to_uchar - convert 2 hex digits to a byte (unsigned char)
53  *
54  * This will ereport() if the end of the string is reached ('\0' found), or if
55  * either character is not a valid hex digit.
56  *
57  * ptr is the pointer to where the digits to convert are in the string, str is
58  * the entire string, which is used only for error reporting.
59  */
60 static inline unsigned char
61 hex2_to_uchar(const unsigned char *ptr, const unsigned char *str)
62 {
63         unsigned char ret = 0;
64         signed char lookup;
65
66         /* Handle the first character */
67         if (*ptr > 127)
68                 goto invalid_input;
69
70         lookup = hexlookup[*ptr];
71         if (lookup < 0)
72                 goto invalid_input;
73
74         ret = lookup << 4;
75
76         /* Move to the second character */
77         ptr++;
78
79         if (*ptr > 127)
80                 goto invalid_input;
81
82         lookup = hexlookup[*ptr];
83         if (lookup < 0)
84                 goto invalid_input;
85
86         ret += lookup;
87
88         return ret;
89
90 invalid_input:
91         ereport(ERROR,
92                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
93                          errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
94                                         str)));
95
96         /* We do not actually reach here */
97         return 0;
98 }
99
100 /*
101  * MAC address (EUI-48 and EUI-64) reader. Accepts several common notations.
102  */
103 Datum
104 macaddr8_in(PG_FUNCTION_ARGS)
105 {
106         const unsigned char *str = (unsigned char *) PG_GETARG_CSTRING(0);
107         const unsigned char *ptr = str;
108         macaddr8   *result;
109         unsigned char a = 0,
110                                 b = 0,
111                                 c = 0,
112                                 d = 0,
113                                 e = 0,
114                                 f = 0,
115                                 g = 0,
116                                 h = 0;
117         int                     count = 0;
118         unsigned char spacer = '\0';
119
120         /* skip leading spaces */
121         while (*ptr && isspace(*ptr))
122                 ptr++;
123
124         /* digits must always come in pairs */
125         while (*ptr && *(ptr + 1))
126         {
127                 /*
128                  * Attempt to decode each byte, which must be 2 hex digits in a row.
129                  * If either digit is not hex, hex2_to_uchar will throw ereport() for
130                  * us.  Either 6 or 8 byte MAC addresses are supported.
131                  */
132
133                 /* Attempt to collect a byte */
134                 count++;
135
136                 switch (count)
137                 {
138                         case 1:
139                                 a = hex2_to_uchar(ptr, str);
140                                 break;
141                         case 2:
142                                 b = hex2_to_uchar(ptr, str);
143                                 break;
144                         case 3:
145                                 c = hex2_to_uchar(ptr, str);
146                                 break;
147                         case 4:
148                                 d = hex2_to_uchar(ptr, str);
149                                 break;
150                         case 5:
151                                 e = hex2_to_uchar(ptr, str);
152                                 break;
153                         case 6:
154                                 f = hex2_to_uchar(ptr, str);
155                                 break;
156                         case 7:
157                                 g = hex2_to_uchar(ptr, str);
158                                 break;
159                         case 8:
160                                 h = hex2_to_uchar(ptr, str);
161                                 break;
162                         default:
163                                 /* must be trailing garbage... */
164                                 ereport(ERROR,
165                                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
166                                 errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
167                                            str)));
168                 }
169
170                 /* Move forward to where the next byte should be */
171                 ptr += 2;
172
173                 /* Check for a spacer, these are valid, anything else is not */
174                 if (*ptr == ':' || *ptr == '-' || *ptr == '.')
175                 {
176                         /* remember the spacer used, if it changes then it isn't valid */
177                         if (spacer == '\0')
178                                 spacer = *ptr;
179
180                         /* Have to use the same spacer throughout */
181                         else if (spacer != *ptr)
182                                 ereport(ERROR,
183                                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
184                                 errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
185                                            str)));
186
187                         /* move past the spacer */
188                         ptr++;
189                 }
190
191                 /* allow trailing whitespace after if we have 6 or 8 bytes */
192                 if (count == 6 || count == 8)
193                 {
194                         if (isspace(*ptr))
195                         {
196                                 while (*++ptr && isspace(*ptr));
197
198                                 /* If we found a space and then non-space, it's invalid */
199                                 if (*ptr)
200                                         ereport(ERROR,
201                                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
202                                                          errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
203                                                                         str)));
204                         }
205                 }
206         }
207
208         /* Convert a 6 byte MAC address to macaddr8 */
209         if (count == 6)
210         {
211                 h = f;
212                 g = e;
213                 f = d;
214
215                 d = 0xFF;
216                 e = 0xFE;
217         }
218         else if (count != 8)
219                 ereport(ERROR,
220                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
221                            errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
222                                           str)));
223
224         result = (macaddr8 *) palloc0(sizeof(macaddr8));
225
226         result->a = a;
227         result->b = b;
228         result->c = c;
229         result->d = d;
230         result->e = e;
231         result->f = f;
232         result->g = g;
233         result->h = h;
234
235         PG_RETURN_MACADDR8_P(result);
236 }
237
238 /*
239  * MAC8 address (EUI-64) output function. Fixed format.
240  */
241 Datum
242 macaddr8_out(PG_FUNCTION_ARGS)
243 {
244         macaddr8   *addr = PG_GETARG_MACADDR8_P(0);
245         char       *result;
246
247         result = (char *) palloc(32);
248
249         snprintf(result, 32, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
250                          addr->a, addr->b, addr->c, addr->d,
251                          addr->e, addr->f, addr->g, addr->h);
252
253         PG_RETURN_CSTRING(result);
254 }
255
256 /*
257  * macaddr8_recv - converts external binary format(EUI-48 and EUI-64) to macaddr8
258  *
259  * The external representation is just the eight bytes, MSB first.
260  */
261 Datum
262 macaddr8_recv(PG_FUNCTION_ARGS)
263 {
264         StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
265         macaddr8   *addr;
266
267         addr = (macaddr8 *) palloc0(sizeof(macaddr8));
268
269         addr->a = pq_getmsgbyte(buf);
270         addr->b = pq_getmsgbyte(buf);
271         addr->c = pq_getmsgbyte(buf);
272
273         if (buf->len == 6)
274         {
275                 addr->d = 0xFF;
276                 addr->e = 0xFE;
277         }
278         else
279         {
280                 addr->d = pq_getmsgbyte(buf);
281                 addr->e = pq_getmsgbyte(buf);
282         }
283
284         addr->f = pq_getmsgbyte(buf);
285         addr->g = pq_getmsgbyte(buf);
286         addr->h = pq_getmsgbyte(buf);
287
288         PG_RETURN_MACADDR8_P(addr);
289 }
290
291 /*
292  * macaddr8_send - converts macaddr8(EUI-64) to binary format
293  */
294 Datum
295 macaddr8_send(PG_FUNCTION_ARGS)
296 {
297         macaddr8   *addr = PG_GETARG_MACADDR8_P(0);
298         StringInfoData buf;
299
300         pq_begintypsend(&buf);
301         pq_sendbyte(&buf, addr->a);
302         pq_sendbyte(&buf, addr->b);
303         pq_sendbyte(&buf, addr->c);
304         pq_sendbyte(&buf, addr->d);
305         pq_sendbyte(&buf, addr->e);
306         pq_sendbyte(&buf, addr->f);
307         pq_sendbyte(&buf, addr->g);
308         pq_sendbyte(&buf, addr->h);
309
310         PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
311 }
312
313
314 /*
315  * macaddr8_cmp_internal - comparison function for sorting:
316  */
317 static int32
318 macaddr8_cmp_internal(macaddr8 *a1, macaddr8 *a2)
319 {
320         if (hibits(a1) < hibits(a2))
321                 return -1;
322         else if (hibits(a1) > hibits(a2))
323                 return 1;
324         else if (lobits(a1) < lobits(a2))
325                 return -1;
326         else if (lobits(a1) > lobits(a2))
327                 return 1;
328         else
329                 return 0;
330 }
331
332 Datum
333 macaddr8_cmp(PG_FUNCTION_ARGS)
334 {
335         macaddr8   *a1 = PG_GETARG_MACADDR8_P(0);
336         macaddr8   *a2 = PG_GETARG_MACADDR8_P(1);
337
338         PG_RETURN_INT32(macaddr8_cmp_internal(a1, a2));
339 }
340
341 /*
342  * Boolean comparison functions.
343  */
344
345 Datum
346 macaddr8_lt(PG_FUNCTION_ARGS)
347 {
348         macaddr8   *a1 = PG_GETARG_MACADDR8_P(0);
349         macaddr8   *a2 = PG_GETARG_MACADDR8_P(1);
350
351         PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) < 0);
352 }
353
354 Datum
355 macaddr8_le(PG_FUNCTION_ARGS)
356 {
357         macaddr8   *a1 = PG_GETARG_MACADDR8_P(0);
358         macaddr8   *a2 = PG_GETARG_MACADDR8_P(1);
359
360         PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) <= 0);
361 }
362
363 Datum
364 macaddr8_eq(PG_FUNCTION_ARGS)
365 {
366         macaddr8   *a1 = PG_GETARG_MACADDR8_P(0);
367         macaddr8   *a2 = PG_GETARG_MACADDR8_P(1);
368
369         PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) == 0);
370 }
371
372 Datum
373 macaddr8_ge(PG_FUNCTION_ARGS)
374 {
375         macaddr8   *a1 = PG_GETARG_MACADDR8_P(0);
376         macaddr8   *a2 = PG_GETARG_MACADDR8_P(1);
377
378         PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) >= 0);
379 }
380
381 Datum
382 macaddr8_gt(PG_FUNCTION_ARGS)
383 {
384         macaddr8   *a1 = PG_GETARG_MACADDR8_P(0);
385         macaddr8   *a2 = PG_GETARG_MACADDR8_P(1);
386
387         PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) > 0);
388 }
389
390 Datum
391 macaddr8_ne(PG_FUNCTION_ARGS)
392 {
393         macaddr8   *a1 = PG_GETARG_MACADDR8_P(0);
394         macaddr8   *a2 = PG_GETARG_MACADDR8_P(1);
395
396         PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) != 0);
397 }
398
399 /*
400  * Support function for hash indexes on macaddr8.
401  */
402 Datum
403 hashmacaddr8(PG_FUNCTION_ARGS)
404 {
405         macaddr8   *key = PG_GETARG_MACADDR8_P(0);
406
407         return hash_any((unsigned char *) key, sizeof(macaddr8));
408 }
409
410 /*
411  * Arithmetic functions: bitwise NOT, AND, OR.
412  */
413 Datum
414 macaddr8_not(PG_FUNCTION_ARGS)
415 {
416         macaddr8   *addr = PG_GETARG_MACADDR8_P(0);
417         macaddr8   *result;
418
419         result = (macaddr8 *) palloc0(sizeof(macaddr8));
420         result->a = ~addr->a;
421         result->b = ~addr->b;
422         result->c = ~addr->c;
423         result->d = ~addr->d;
424         result->e = ~addr->e;
425         result->f = ~addr->f;
426         result->g = ~addr->g;
427         result->h = ~addr->h;
428
429         PG_RETURN_MACADDR8_P(result);
430 }
431
432 Datum
433 macaddr8_and(PG_FUNCTION_ARGS)
434 {
435         macaddr8   *addr1 = PG_GETARG_MACADDR8_P(0);
436         macaddr8   *addr2 = PG_GETARG_MACADDR8_P(1);
437         macaddr8   *result;
438
439         result = (macaddr8 *) palloc0(sizeof(macaddr8));
440         result->a = addr1->a & addr2->a;
441         result->b = addr1->b & addr2->b;
442         result->c = addr1->c & addr2->c;
443         result->d = addr1->d & addr2->d;
444         result->e = addr1->e & addr2->e;
445         result->f = addr1->f & addr2->f;
446         result->g = addr1->g & addr2->g;
447         result->h = addr1->h & addr2->h;
448
449         PG_RETURN_MACADDR8_P(result);
450 }
451
452 Datum
453 macaddr8_or(PG_FUNCTION_ARGS)
454 {
455         macaddr8   *addr1 = PG_GETARG_MACADDR8_P(0);
456         macaddr8   *addr2 = PG_GETARG_MACADDR8_P(1);
457         macaddr8   *result;
458
459         result = (macaddr8 *) palloc0(sizeof(macaddr8));
460         result->a = addr1->a | addr2->a;
461         result->b = addr1->b | addr2->b;
462         result->c = addr1->c | addr2->c;
463         result->d = addr1->d | addr2->d;
464         result->e = addr1->e | addr2->e;
465         result->f = addr1->f | addr2->f;
466         result->g = addr1->g | addr2->g;
467         result->h = addr1->h | addr2->h;
468
469         PG_RETURN_MACADDR8_P(result);
470 }
471
472 /*
473  * Truncation function to allow comparing macaddr8 manufacturers.
474  */
475 Datum
476 macaddr8_trunc(PG_FUNCTION_ARGS)
477 {
478         macaddr8   *addr = PG_GETARG_MACADDR8_P(0);
479         macaddr8   *result;
480
481         result = (macaddr8 *) palloc0(sizeof(macaddr8));
482
483         result->a = addr->a;
484         result->b = addr->b;
485         result->c = addr->c;
486         result->d = 0;
487         result->e = 0;
488         result->f = 0;
489         result->g = 0;
490         result->h = 0;
491
492         PG_RETURN_MACADDR8_P(result);
493 }
494
495 /*
496  * Set 7th bit for modified EUI-64 as used in IPv6.
497  */
498 Datum
499 macaddr8_set7bit(PG_FUNCTION_ARGS)
500 {
501         macaddr8   *addr = PG_GETARG_MACADDR8_P(0);
502         macaddr8   *result;
503
504         result = (macaddr8 *) palloc0(sizeof(macaddr8));
505
506         result->a = addr->a | 0x02;
507         result->b = addr->b;
508         result->c = addr->c;
509         result->d = addr->d;
510         result->e = addr->e;
511         result->f = addr->f;
512         result->g = addr->g;
513         result->h = addr->h;
514
515         PG_RETURN_MACADDR8_P(result);
516 }
517
518 /*----------------------------------------------------------
519  *      Conversion operators.
520  *---------------------------------------------------------*/
521
522 Datum
523 macaddrtomacaddr8(PG_FUNCTION_ARGS)
524 {
525         macaddr    *addr6 = PG_GETARG_MACADDR_P(0);
526         macaddr8   *result;
527
528         result = (macaddr8 *) palloc0(sizeof(macaddr8));
529
530         result->a = addr6->a;
531         result->b = addr6->b;
532         result->c = addr6->c;
533         result->d = 0xFF;
534         result->e = 0xFE;
535         result->f = addr6->d;
536         result->g = addr6->e;
537         result->h = addr6->f;
538
539
540         PG_RETURN_MACADDR8_P(result);
541 }
542
543 Datum
544 macaddr8tomacaddr(PG_FUNCTION_ARGS)
545 {
546         macaddr8   *addr = PG_GETARG_MACADDR8_P(0);
547         macaddr    *result;
548
549         result = (macaddr *) palloc0(sizeof(macaddr));
550
551         if ((addr->d != 0xFF) || (addr->e != 0xFE))
552                 ereport(ERROR,
553                                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
554                                  errmsg("macaddr8 data out of range to convert to macaddr"),
555                            errhint("Only addresses that have FF and FE as values in the "
556                                            "4th and 5th bytes, from the left, for example: "
557                                          "XX-XX-XX-FF-FE-XX-XX-XX, are eligible to be converted "
558                                            "from macaddr8 to macaddr.")));
559
560         result->a = addr->a;
561         result->b = addr->b;
562         result->c = addr->c;
563         result->d = addr->f;
564         result->e = addr->g;
565         result->f = addr->h;
566
567         PG_RETURN_MACADDR_P(result);
568 }