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