1 /*
2  * Python UUID module that wraps libuuid or Windows rpcrt4.dll.
3  * DCE compatible Universally Unique Identifier library.
4  */
5 
6 #define PY_SSIZE_T_CLEAN
7 
8 #include "Python.h"
9 #if defined(HAVE_UUID_H)
10   // AIX, FreeBSD, libuuid with pkgconf
11   #include <uuid.h>
12 #elif defined(HAVE_UUID_UUID_H)
13   // libuuid without pkgconf
14   #include <uuid/uuid.h>
15 #endif
16 
17 #ifdef MS_WINDOWS
18 #include <rpc.h>
19 #endif
20 
21 #ifndef MS_WINDOWS
22 
23 static PyObject *
py_uuid_generate_time_safe(PyObject * Py_UNUSED (context),PyObject * Py_UNUSED (ignored))24 py_uuid_generate_time_safe(PyObject *Py_UNUSED(context),
25                            PyObject *Py_UNUSED(ignored))
26 {
27     uuid_t uuid;
28 #ifdef HAVE_UUID_GENERATE_TIME_SAFE
29     int res;
30 
31     res = uuid_generate_time_safe(uuid);
32     return Py_BuildValue("y#i", (const char *) uuid, sizeof(uuid), res);
33 #elif defined(HAVE_UUID_CREATE)
34     uint32_t status;
35     uuid_create(&uuid, &status);
36 # if defined(HAVE_UUID_ENC_BE)
37     unsigned char buf[sizeof(uuid)];
38     uuid_enc_be(buf, &uuid);
39     return Py_BuildValue("y#i", buf, sizeof(uuid), (int) status);
40 # else
41     return Py_BuildValue("y#i", (const char *) &uuid, sizeof(uuid), (int) status);
42 # endif /* HAVE_UUID_CREATE */
43 #else /* HAVE_UUID_GENERATE_TIME_SAFE */
44     uuid_generate_time(uuid);
45     return Py_BuildValue("y#O", (const char *) uuid, sizeof(uuid), Py_None);
46 #endif /* HAVE_UUID_GENERATE_TIME_SAFE */
47 }
48 
49 #else /* MS_WINDOWS */
50 
51 static PyObject *
py_UuidCreate(PyObject * Py_UNUSED (context),PyObject * Py_UNUSED (ignored))52 py_UuidCreate(PyObject *Py_UNUSED(context),
53               PyObject *Py_UNUSED(ignored))
54 {
55     UUID uuid;
56     RPC_STATUS res;
57 
58     Py_BEGIN_ALLOW_THREADS
59     res = UuidCreateSequential(&uuid);
60     Py_END_ALLOW_THREADS
61 
62     switch (res) {
63     case RPC_S_OK:
64     case RPC_S_UUID_LOCAL_ONLY:
65     case RPC_S_UUID_NO_ADDRESS:
66         /*
67         All success codes, but the latter two indicate that the UUID is random
68         rather than based on the MAC address. If the OS can't figure this out,
69         neither can we, so we'll take it anyway.
70         */
71         return Py_BuildValue("y#", (const char *)&uuid, sizeof(uuid));
72     }
73     PyErr_SetFromWindowsErr(res);
74     return NULL;
75 }
76 
77 #endif /* MS_WINDOWS */
78 
79 
80 static int
uuid_exec(PyObject * module)81 uuid_exec(PyObject *module) {
82     assert(sizeof(uuid_t) == 16);
83 #if defined(MS_WINDOWS)
84     int has_uuid_generate_time_safe = 0;
85 #elif defined(HAVE_UUID_GENERATE_TIME_SAFE)
86     int has_uuid_generate_time_safe = 1;
87 #else
88     int has_uuid_generate_time_safe = 0;
89 #endif
90     if (PyModule_AddIntConstant(module, "has_uuid_generate_time_safe",
91                                 has_uuid_generate_time_safe) < 0) {
92         return -1;
93     }
94     return 0;
95 }
96 
97 static PyMethodDef uuid_methods[] = {
98 #if defined(HAVE_UUID_UUID_H) || defined(HAVE_UUID_H)
99     {"generate_time_safe", py_uuid_generate_time_safe, METH_NOARGS, NULL},
100 #endif
101 #if defined(MS_WINDOWS)
102     {"UuidCreate", py_UuidCreate, METH_NOARGS, NULL},
103 #endif
104     {NULL, NULL, 0, NULL}           /* sentinel */
105 };
106 
107 static PyModuleDef_Slot uuid_slots[] = {
108     {Py_mod_exec, uuid_exec},
109     {0, NULL}
110 };
111 
112 static struct PyModuleDef uuidmodule = {
113     PyModuleDef_HEAD_INIT,
114     .m_name = "_uuid",
115     .m_size = 0,
116     .m_methods = uuid_methods,
117     .m_slots = uuid_slots,
118 };
119 
120 PyMODINIT_FUNC
PyInit__uuid(void)121 PyInit__uuid(void)
122 {
123     return PyModuleDef_Init(&uuidmodule);
124 }
125