7162880c32c0a5257988f8a3896761ae19909304
[samba.git] / libcli / nbt / pynbt.c
1 /*
2    Unix SMB/CIFS implementation.
3    Samba utility functions
4    Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program 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
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <Python.h>
21 #include "includes.h"
22 #include "libcli/util/pyerrors.h"
23 #include "python/modules.h"
24 #include "../libcli/nbt/libnbt.h"
25 #include "lib/events/events.h"
26
27 void initnetbios(void);
28
29 extern PyTypeObject nbt_node_Type;
30
31 typedef struct {
32         PyObject_HEAD
33         TALLOC_CTX *mem_ctx;
34         struct nbt_name_socket *socket;
35 } nbt_node_Object;
36
37 static void py_nbt_node_dealloc(nbt_node_Object *self)
38 {
39         talloc_free(self->mem_ctx);
40         self->ob_type->tp_free(self);
41 }
42
43 static PyObject *py_nbt_node_init(PyTypeObject *self, PyObject *args, PyObject *kwargs)
44 {
45         struct tevent_context *ev;
46         nbt_node_Object *ret = PyObject_New(nbt_node_Object, &nbt_node_Type);
47
48         ret->mem_ctx = talloc_new(NULL);
49         if (ret->mem_ctx == NULL)
50                 return NULL;
51
52         ev = s4_event_context_init(ret->mem_ctx);
53         ret->socket = nbt_name_socket_init(ret->mem_ctx, ev);
54         return (PyObject *)ret;
55 }
56
57 static bool PyObject_AsDestinationTuple(PyObject *obj, const char **dest_addr, uint16_t *dest_port)
58 {
59         if (PyString_Check(obj)) {
60                 *dest_addr = PyString_AsString(obj);
61                 *dest_port = NBT_NAME_SERVICE_PORT;
62                 return true;
63         }
64
65         if (PyTuple_Check(obj)) {
66                 if (PyTuple_Size(obj) < 1) {
67                         PyErr_SetString(PyExc_TypeError, "Destination tuple size invalid");
68                         return false;
69                 }
70
71                 if (!PyString_Check(PyTuple_GetItem(obj, 0))) {
72                         PyErr_SetString(PyExc_TypeError, "Destination tuple first element not string");
73                         return false;
74                 }
75
76                 *dest_addr = PyString_AsString(obj);
77
78                 if (PyTuple_Size(obj) == 1) {
79                         *dest_port = NBT_NAME_SERVICE_PORT;
80                         return true;
81                 } else if (PyInt_Check(PyTuple_GetItem(obj, 1))) {
82                         *dest_port = PyInt_AsLong(PyTuple_GetItem(obj, 1));
83                         return true;
84                 } else {
85                         PyErr_SetString(PyExc_TypeError, "Destination tuple second element not a port");
86                         return false;
87                 }
88         }
89
90         PyErr_SetString(PyExc_TypeError, "Destination tuple second element not a port");
91         return false;
92 }
93
94 static bool PyObject_AsNBTName(PyObject *obj, struct nbt_name_socket *name_socket, struct nbt_name *name)
95 {
96         if (PyTuple_Check(obj)) {
97                 if (PyTuple_Size(obj) == 2) {
98                         name->name = PyString_AsString(PyTuple_GetItem(obj, 0));
99                         name->type = PyInt_AsLong(PyTuple_GetItem(obj, 1));
100                         name->scope = NULL;
101                         return true;
102                 } else if (PyTuple_Size(obj) == 3) {
103                         name->name = PyString_AsString(PyTuple_GetItem(obj, 0));
104                         name->scope = PyString_AsString(PyTuple_GetItem(obj, 1));
105                         name->type = PyInt_AsLong(PyTuple_GetItem(obj, 2));
106                         return true;
107                 } else {
108                         PyErr_SetString(PyExc_TypeError, "Invalid tuple size");
109                         return false;
110                 }
111         }
112
113         if (PyString_Check(obj)) {
114                 /* FIXME: Parse string to be able to interpret things like RHONWYN<02> ? */
115                 name->name = PyString_AsString(obj);
116                 name->scope = NULL;
117                 name->type = 0;
118                 return true;
119         }
120
121         PyErr_SetString(PyExc_TypeError, "Invalid type for object");
122         return false;
123 }
124
125 static PyObject *PyObject_FromNBTName(struct nbt_name_socket *name_socket, 
126                                       struct nbt_name *name)
127 {
128         if (name->scope) {
129                 return Py_BuildValue("(ssi)", name->name, name->scope, name->type);
130         } else {
131                 return Py_BuildValue("(si)", name->name, name->type);
132         }
133 }
134
135 static PyObject *py_nbt_name_query(PyObject *self, PyObject *args, PyObject *kwargs)
136 {
137         nbt_node_Object *node = (nbt_node_Object *)self;
138         PyObject *ret, *reply_addrs, *py_dest, *py_name;
139         struct nbt_name_query io;
140         NTSTATUS status;
141         int i;
142
143         const char *kwnames[] = { "name", "dest", "broadcast", "wins", "timeout",
144                                   "retries", NULL };
145         io.in.broadcast = true;
146         io.in.wins_lookup = false;
147         io.in.timeout = 0;
148         io.in.retries = 3;
149
150         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|bbii:query_name",
151                                          discard_const_p(char *, kwnames),
152                                          &py_name, &py_dest,
153                                          &io.in.broadcast, &io.in.wins_lookup,
154                                          &io.in.timeout, &io.in.retries)) {
155                 return NULL;
156         }
157
158         if (!PyObject_AsDestinationTuple(py_dest, &io.in.dest_addr, &io.in.dest_port))
159                 return NULL;
160
161         if (!PyObject_AsNBTName(py_name, node->socket, &io.in.name))
162                 return NULL;
163
164         status = nbt_name_query(node->socket, NULL, &io);
165
166         if (NT_STATUS_IS_ERR(status)) {
167                 PyErr_SetNTSTATUS(status);
168                 return NULL;
169         }
170
171         ret = PyTuple_New(3);
172         if (ret == NULL)
173                 return NULL;
174         PyTuple_SetItem(ret, 0, PyString_FromString(io.out.reply_from));
175
176         py_name = PyObject_FromNBTName(node->socket, &io.out.name);
177         if (py_name == NULL)
178                 return NULL;
179
180         PyTuple_SetItem(ret, 1, py_name);
181
182         reply_addrs = PyList_New(io.out.num_addrs);
183         if (reply_addrs == NULL) {
184                 Py_DECREF(ret);
185                 return NULL;
186         }
187
188         for (i = 0; i < io.out.num_addrs; i++) {
189                 PyList_SetItem(reply_addrs, i, PyString_FromString(io.out.reply_addrs[i]));
190         }
191
192         PyTuple_SetItem(ret, 2, reply_addrs);
193         return ret;
194 }
195
196 static PyObject *py_nbt_name_status(PyObject *self, PyObject *args, PyObject *kwargs)
197 {
198         nbt_node_Object *node = (nbt_node_Object *)self;
199         PyObject *ret, *py_dest, *py_name, *py_names;
200         struct nbt_name_status io;
201         int i;
202         NTSTATUS status;
203
204         const char *kwnames[] = { "name", "dest", "timeout", "retries", NULL };
205
206         io.in.timeout = 0;
207         io.in.retries = 0;
208
209         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|ii:name_status",
210                                          discard_const_p(char *, kwnames),
211                                          &py_name, &py_dest,
212                                          &io.in.timeout, &io.in.retries)) {
213                 return NULL;
214         }
215
216         if (!PyObject_AsDestinationTuple(py_dest, &io.in.dest_addr, &io.in.dest_port))
217                 return NULL;
218
219         if (!PyObject_AsNBTName(py_name, node->socket, &io.in.name))
220                 return NULL;
221
222         status = nbt_name_status(node->socket, NULL, &io);
223
224         if (NT_STATUS_IS_ERR(status)) {
225                 PyErr_SetNTSTATUS(status);
226                 return NULL;
227         }
228
229         ret = PyTuple_New(3);
230         if (ret == NULL)
231                 return NULL;
232         PyTuple_SetItem(ret, 0, PyString_FromString(io.out.reply_from));
233
234         py_name = PyObject_FromNBTName(node->socket, &io.out.name);
235         if (py_name == NULL)
236                 return NULL;
237
238         PyTuple_SetItem(ret, 1, py_name);
239
240         py_names = PyList_New(io.out.status.num_names);
241
242         for (i = 0; i < io.out.status.num_names; i++) {
243                 PyList_SetItem(py_names, i, Py_BuildValue("(sii)",
244                                 io.out.status.names[i].name,
245                                 io.out.status.names[i].nb_flags,
246                                 io.out.status.names[i].type));
247         }
248
249         PyTuple_SetItem(ret, 2, py_names);
250
251         return ret;
252 }
253
254 static PyObject *py_nbt_name_register(PyObject *self, PyObject *args, PyObject *kwargs)
255 {
256         nbt_node_Object *node = (nbt_node_Object *)self;
257         PyObject *ret, *py_dest, *py_name;
258         struct nbt_name_register io;
259         NTSTATUS status;
260
261         const char *kwnames[] = { "name", "address", "dest", "register_demand", "broadcast",
262                                   "multi_homed", "ttl", "timeout", "retries", NULL };
263
264         io.in.broadcast = true;
265         io.in.multi_homed = true;
266         io.in.register_demand = true;
267         io.in.timeout = 0;
268         io.in.retries = 0;
269
270         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OsO|bbbiii:query_name",
271                                          discard_const_p(char *, kwnames),
272                                          &py_name, &io.in.address, &py_dest,
273                                          &io.in.register_demand,
274                                          &io.in.broadcast, &io.in.multi_homed,
275                                          &io.in.ttl, &io.in.timeout, &io.in.retries)) {
276                 return NULL;
277         }
278
279         if (!PyObject_AsDestinationTuple(py_dest, &io.in.dest_addr, &io.in.dest_port))
280                 return NULL;
281
282         if (!PyObject_AsNBTName(py_name, node->socket, &io.in.name))
283                 return NULL;
284
285         status = nbt_name_register(node->socket, NULL, &io);
286
287         if (NT_STATUS_IS_ERR(status)) {
288                 PyErr_SetNTSTATUS(status);
289                 return NULL;
290         }
291
292         ret = PyTuple_New(3);
293         if (ret == NULL)
294                 return NULL;
295         PyTuple_SetItem(ret, 0, PyString_FromString(io.out.reply_from));
296
297         py_name = PyObject_FromNBTName(node->socket, &io.out.name);
298         if (py_name == NULL)
299                 return NULL;
300
301         PyTuple_SetItem(ret, 1, py_name);
302
303         PyTuple_SetItem(ret, 2, PyString_FromString(io.out.reply_addr));
304
305         PyTuple_SetItem(ret, 3, PyInt_FromLong(io.out.rcode));
306
307         return ret;
308 }
309
310 static PyObject *py_nbt_name_refresh(PyObject *self, PyObject *args, PyObject *kwargs)
311 {
312         nbt_node_Object *node = (nbt_node_Object *)self;
313         PyObject *ret, *py_dest, *py_name;
314         struct nbt_name_refresh io;
315         NTSTATUS status;
316
317         const char *kwnames[] = { "name", "address", "dest", "nb_flags", "broadcast",
318                                   "ttl", "timeout", "retries", NULL };
319
320         io.in.broadcast = true;
321         io.in.nb_flags = 0;
322         io.in.timeout = 0;
323         io.in.retries = 0;
324
325         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OsO|ibiii:query_name",
326                                          discard_const_p(char *, kwnames),
327                                          &py_name, &io.in.address, &py_dest,
328                                          &io.in.nb_flags,
329                                          &io.in.broadcast,
330                                          &io.in.ttl, &io.in.timeout, &io.in.retries)) {
331                 return NULL;
332         }
333
334         if (!PyObject_AsDestinationTuple(py_dest, &io.in.dest_addr, &io.in.dest_port))
335                 return NULL;
336
337         if (!PyObject_AsNBTName(py_name, node->socket, &io.in.name))
338                 return NULL;
339
340         status = nbt_name_refresh(node->socket, NULL, &io);
341
342         if (NT_STATUS_IS_ERR(status)) {
343                 PyErr_SetNTSTATUS(status);
344                 return NULL;
345         }
346
347         ret = PyTuple_New(3);
348         if (ret == NULL)
349                 return NULL;
350         PyTuple_SetItem(ret, 0, PyString_FromString(io.out.reply_from));
351
352         py_name = PyObject_FromNBTName(node->socket, &io.out.name);
353         if (py_name == NULL)
354                 return NULL;
355
356         PyTuple_SetItem(ret, 1, py_name);
357
358         PyTuple_SetItem(ret, 2, PyString_FromString(io.out.reply_addr));
359
360         PyTuple_SetItem(ret, 3, PyInt_FromLong(io.out.rcode));
361
362         return ret;
363 }
364
365 static PyObject *py_nbt_name_release(PyObject *self, PyObject *args, PyObject *kwargs)
366 {
367         Py_RETURN_NONE; /* FIXME */
368 }
369
370 static PyMethodDef py_nbt_methods[] = {
371         { "query_name", (PyCFunction)py_nbt_name_query, METH_VARARGS|METH_KEYWORDS,
372                 "S.query_name(name, dest, broadcast=True, wins=False, timeout=0, retries=3) -> (reply_from, name, reply_addr)\n"
373                 "Query for a NetBIOS name" },
374         { "register_name", (PyCFunction)py_nbt_name_register, METH_VARARGS|METH_KEYWORDS,
375                 "S.register_name(name, address, dest, register_demand=True, broadcast=True, multi_homed=True, ttl=0, timeout=0, retries=0) -> (reply_from, name, reply_addr, rcode)\n"
376                 "Register a new name" },
377         { "release_name", (PyCFunction)py_nbt_name_release, METH_VARARGS|METH_KEYWORDS, "S.release_name(name, address, dest, nb_flags=0, broadcast=true, timeout=0, retries=3) -> (reply_from, name, reply_addr, rcode)\n"
378                 "release a previously registered name" },
379         { "refresh_name", (PyCFunction)py_nbt_name_refresh, METH_VARARGS|METH_KEYWORDS, "S.refresh_name(name, address, dest, nb_flags=0, broadcast=True, ttl=0, timeout=0, retries=0) -> (reply_from, name, reply_addr, rcode)\n"
380                 "release a previously registered name" },
381         { "name_status", (PyCFunction)py_nbt_name_status, METH_VARARGS|METH_KEYWORDS,
382                 "S.name_status(name, dest, timeout=0, retries=0) -> (reply_from, name, status)\n"
383                 "Find the status of a name" },
384
385         { NULL }
386 };
387
388 PyTypeObject nbt_node_Type = {
389         PyObject_HEAD_INIT(NULL) 0,
390         .tp_name = "netbios.Node",
391         .tp_basicsize = sizeof(nbt_node_Object),
392         .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
393         .tp_new = py_nbt_node_init,
394         .tp_dealloc = (destructor)py_nbt_node_dealloc,
395         .tp_methods = py_nbt_methods,
396         .tp_doc = "Node()\n"
397                   "Create a new NetBIOS node\n"
398 };
399
400 void initnetbios(void)
401 {
402         PyObject *mod;
403         if (PyType_Ready(&nbt_node_Type) < 0)
404                 return;
405
406         mod = Py_InitModule3("netbios", NULL, "NetBIOS over TCP/IP support");
407
408         Py_INCREF((PyObject *)&nbt_node_Type);
409         PyModule_AddObject(mod, "Node", (PyObject *)&nbt_node_Type);
410 }