source: psi/trunk/cutestuff/rtsp/altports.cpp

Last change on this file was 2, checked in by dmik, 19 years ago

Imported original Psi 0.10 sources from Affinix

File size: 8.7 KB
Line 
1/*
2 * altports.cpp - manage alternative UDP port ranges
3 * Copyright (C) 2004 Justin Karneges
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include "altports.h"
22
23#include <qsocketdevice.h>
24#include <qsocketnotifier.h>
25#include <qptrlist.h>
26
27//----------------------------------------------------------------------------
28// PortRange
29//----------------------------------------------------------------------------
30PortRange::PortRange()
31{
32 base = 0;
33 count = 0;
34}
35
36QString PortRange::toString() const
37{
38 QString s;
39 if(count > 0)
40 {
41 s += QString::number(base);
42 if(count > 1)
43 {
44 s += '-';
45 s += QString::number(base + count - 1);
46 }
47 }
48 return s;
49}
50
51bool PortRange::fromString(const QString &s)
52{
53 if(s.isEmpty())
54 return false;
55
56 int n = s.find('-');
57 if(n == -1)
58 {
59 // single port
60 base = s.toInt();
61 count = 1;
62 }
63 else
64 {
65 // port range
66 int first = s.mid(0, n).toInt();
67 int last = s.mid(n + 1).toInt();
68 if(first < 1 || first > 65535 || last < first || last > 65535)
69 return false;
70
71 base = first;
72 count = last - first + 1;
73 }
74 return true;
75}
76
77//----------------------------------------------------------------------------
78// PortRangeList
79//----------------------------------------------------------------------------
80void PortRangeList::merge(const PortRange &r)
81{
82 // see if we have this base already
83 for(Iterator it = begin(); it != end(); ++it)
84 {
85 PortRange &pr = *it;
86 if(pr.base == r.base)
87 {
88 pr.count = QMAX(pr.count, r.count);
89 return;
90 }
91 }
92
93 // else, just append it
94 append(r);
95}
96
97int PortRangeList::findByBase(int base) const
98{
99 int index = 0;
100 for(ConstIterator it = begin(); it != end(); ++it)
101 {
102 if((*it).base == base)
103 return index;
104 ++index;
105 }
106 return -1;
107}
108
109//----------------------------------------------------------------------------
110// UDPItem
111//----------------------------------------------------------------------------
112class UDPItem : public QObject
113{
114 Q_OBJECT
115public:
116 static UDPItem *create(const QHostAddress &addr, int port)
117 {
118 QSocketDevice *sd = new QSocketDevice(QSocketDevice::Datagram);
119 sd->setBlocking(false);
120 if(!sd->bind(addr, port))
121 return 0;
122 UDPItem *i = new UDPItem;
123 i->sd = sd;
124 i->sn = new QSocketNotifier(i->sd->socket(), QSocketNotifier::Read);
125 i->connect(i->sn, SIGNAL(activated(int)), SLOT(sn_activated(int)));
126 i->_port = port;
127 //printf("UDP BIND: [%d]\n", port);
128 return i;
129 }
130
131 ~UDPItem()
132 {
133 delete sn;
134 delete sd;
135 //printf("UDP UNBIND: [%d]\n", _port);
136 }
137
138 int port() const
139 {
140 return _port;
141 }
142
143 void write(const QByteArray &buf, const QHostAddress &addr, int port)
144 {
145 sd->setBlocking(true);
146 sd->writeBlock(buf.data(), buf.size(), addr, port);
147 sd->setBlocking(false);
148 }
149
150signals:
151 void packetReady(const QByteArray &buf, const QHostAddress &addr, int port);
152
153private slots:
154 void sn_activated(int)
155 {
156 QByteArray buf(8192);
157 int actual = sd->readBlock(buf.data(), buf.size());
158 buf.resize(actual);
159 QHostAddress pa = sd->peerAddress();
160 int pp = sd->peerPort();
161 packetReady(buf, pa, pp);
162 }
163
164private:
165 UDPItem()
166 {
167 }
168
169 QSocketDevice *sd;
170 QSocketNotifier *sn;
171 int _port;
172};
173
174//----------------------------------------------------------------------------
175// PortRange
176//----------------------------------------------------------------------------
177#define PORT_ALLOC_BASE 16000
178#define PORT_ALLOC_MAX 65535
179
180class PortSequence : public QObject
181{
182 Q_OBJECT
183public:
184 PortSequence()
185 {
186 list.setAutoDelete(true);
187 }
188
189 void reset()
190 {
191 list.clear();
192 }
193
194 bool allocate(const QHostAddress &address, int count, int base=-1)
195 {
196 return try_allocate(address, count, base);
197 }
198
199 bool resize(int count)
200 {
201 // don't allow growing
202 if(count > (int)list.count())
203 return false;
204
205 QPtrListIterator<UDPItem> it(list);
206 it += count;
207 int del = list.count() - count;
208 for(int n = 0; n < del; ++n)
209 list.removeRef(it.current());
210 return true;
211 }
212
213 void send(int index, const QHostAddress &addr, int destPort, const QByteArray &buf)
214 {
215 if(index >= 0 && index < (int)list.count())
216 list.at(index)->write(buf, addr, destPort);
217 }
218
219 QHostAddress address() const
220 {
221 return addr;
222 }
223
224 int base() const
225 {
226 if(!list.isEmpty())
227 return list.getFirst()->port();
228 else
229 return -1;
230 }
231
232 int count() const
233 {
234 return list.count();
235 }
236
237signals:
238 void packetReady(int index, const QHostAddress &addr, int port, const QByteArray &buf);
239
240private slots:
241 void udp_packetReady(const QByteArray &buf, const QHostAddress &addr, int port)
242 {
243 UDPItem *su = (UDPItem *)sender();
244 bool found = false;
245 int index = 0;
246 QPtrListIterator<UDPItem> it(list);
247 for(UDPItem *u; (u = it.current()); ++it)
248 {
249 if(u == su)
250 {
251 found = true;
252 break;
253 }
254 ++index;
255 }
256 if(!found)
257 return;
258
259 packetReady(index, addr, port, buf);
260 }
261
262private:
263 QHostAddress addr;
264 QPtrList<UDPItem> list;
265
266 bool try_allocate(const QHostAddress &address, int count, int base)
267 {
268 reset();
269 if(count < 1)
270 return true;
271
272 int start = base != -1 ? base : PORT_ALLOC_BASE;
273 while(start + (count - 1) <= PORT_ALLOC_MAX)
274 {
275 QPtrList<UDPItem> udplist;
276 bool ok = true;
277 for(int n = 0; n < count; ++n)
278 {
279 UDPItem *i = UDPItem::create(address, start + n);
280 if(!i)
281 {
282 ok = false;
283 break;
284 }
285 udplist.append(i);
286 }
287 if(ok)
288 {
289 list = udplist;
290 break;
291 }
292 udplist.setAutoDelete(true);
293
294 // if using 'start', we only get one chance
295 if(base != -1)
296 break;
297
298 start += udplist.count() + 1;
299 }
300
301 if(list.isEmpty())
302 return false;
303
304 addr = address;
305
306 QPtrListIterator<UDPItem> it(list);
307 for(UDPItem *u; (u = it.current()); ++it)
308 connect(u, SIGNAL(packetReady(const QByteArray &, const QHostAddress &, int)), SLOT(udp_packetReady(const QByteArray &, const QHostAddress &, int)));
309
310 return true;
311 }
312};
313
314//----------------------------------------------------------------------------
315// AltPorts
316//----------------------------------------------------------------------------
317class AltPorts::Private : public QObject
318{
319 Q_OBJECT
320public:
321 AltPorts *par;
322 QPtrList<PortSequence> list;
323 PortSequence *ports;
324 PortRangeList orig;
325
326 Private(AltPorts *_par)
327 {
328 par = _par;
329 ports = 0;
330 }
331
332public slots:
333 void range_packetReady(int index, const QHostAddress &addr, int port, const QByteArray &buf)
334 {
335 par->packetReady(index, addr, port, buf);
336 }
337};
338
339AltPorts::AltPorts()
340{
341 d = new Private(this);
342}
343
344AltPorts::~AltPorts()
345{
346 delete d;
347}
348
349void AltPorts::reset()
350{
351 d->list.clear();
352 delete d->ports;
353 d->ports = 0;
354 d->orig.clear();
355}
356
357bool AltPorts::isEmpty() const
358{
359 if(!d->ports && d->list.isEmpty())
360 return true;
361 return false;
362}
363
364bool AltPorts::allocate(const PortRange &real, PortRange *alt)
365{
366 if(!isEmpty())
367 return false;
368
369 PortRangeList in, out;
370 in.append(real);
371 if(!reserve(in, &out))
372 return false;
373 keep(real);
374 *alt = out.first();
375 return true;
376}
377
378bool AltPorts::reserve(const PortRangeList &real, PortRangeList *alt)
379{
380 if(!isEmpty())
381 return false;
382
383 PortRangeList out;
384 QPtrList<PortSequence> list;
385 for(PortRangeList::ConstIterator it = real.begin(); it != real.end(); ++it)
386 {
387 PortSequence *s = new PortSequence;
388 if(!s->allocate(QHostAddress(), (*it).count))
389 {
390 delete s;
391 list.setAutoDelete(true);
392 return false;
393 }
394 PortRange r;
395 r.base = s->base();
396 r.count = s->count();
397 out.append(r);
398 list.append(s);
399 }
400
401 d->list = list;
402 d->orig = real;
403 *alt = out;
404 return true;
405}
406
407void AltPorts::keep(const PortRange &r)
408{
409 if(d->ports)
410 return;
411
412 int n = d->orig.findByBase(r.base);
413 if(n == -1)
414 return;
415
416 PortSequence *s = d->list.at(n);
417 s->resize(r.count);
418 d->ports = s;
419 d->list.removeRef(s);
420 d->list.setAutoDelete(true);
421 d->list.clear();
422 d->list.setAutoDelete(false);
423 connect(d->ports, SIGNAL(packetReady(int, const QHostAddress &, int, const QByteArray &)), d, SLOT(range_packetReady(int, const QHostAddress &, int, const QByteArray &)));
424}
425
426PortRange AltPorts::range() const
427{
428 PortRange r;
429 if(d->ports)
430 {
431 r.base = d->ports->base();
432 r.count = d->ports->count();
433 }
434 return r;
435}
436
437void AltPorts::send(int index, const QHostAddress &addr, int destPort, const QByteArray &buf)
438{
439 if(d->ports)
440 d->ports->send(index, addr, destPort, buf);
441}
442
443#include "altports.moc"
Note: See TracBrowser for help on using the repository browser.