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