mIRC Home    About    Download    Register    News    Help

Print Thread
#228178 11/12/10 06:21 AM
Joined: Jul 2006
Posts: 4,149
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,149
When opening two udp sockets in the same scope, mirc select the same local port if we don't use one :
Code:
on *:udpread:test:{
  sockread -fn 4096 &a
  tokenize 32 $bvar(&a,1,4096).text 
  var -s %ip $sock(test).saddr ,%port $sock(test).sport ,%d $1-
}
Type
Code:
/sockudp -kn test 8001 127.0.0.1 8001
then
Code:
//sockudp -kn test1 127.0.0.1 8001 lol | sockudp -kn test2 127.0.0.1 8001 lol


can't reproduce the behavior with :
Code:
//sockudp -kn test1 127.0.0.1 8001 lol | .timer -m 1 500 sockudp -kn test2 127.0.0.1 8001 lol


tested on mIRC 7.16

Last edited by Wims; 11/12/10 06:24 AM.

#mircscripting @ irc.swiftirc.net == the best mIRC help channel
Joined: Dec 2002
Posts: 344
D
Pan-dimensional mouse
Offline
Pan-dimensional mouse
D
Joined: Dec 2002
Posts: 344
Originally Posted By: Wims
When opening two udp sockets in the same scope, mirc select the same local port if we don't use one :


mIRC is not actually re-using the same local port. The problem is that $sock().sport returns "the source ... port of the last received UDP packet". The event "ON UDPREAD" doesn't get an opportunity to execute until after both /sockudp commands have been issued. By the time the event does execute (in response to the first UDP packet), the last received UDP packet is already the second one. Unfortunately, this is a limitation of the way mIRC handles sockets. If you don't process the UDP packet before another UDP packet is received, you will lose the information about the UDP packets prior to the most recently received.

Joined: Jul 2006
Posts: 4,149
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,149
Hum, you might be right, but :
Quote:
By the time the event does execute (in response to the first UDP packet), the last received UDP packet is already the second one.
I think this is wrong, why would it work with another delay ? The event triggers twice whatever the order of the packet, in both case the last received udp packet is the one I want to play with but the source port is the same, and in my case it's the same ip too, how to send an information to the right person ?

If I specify the local port to use, with a function like this:
Code:
alias getfreeport {
  var %a 5000
  while (!$portfree(%a)) inc %a
  return %a
}
it work perfectly, and this is what I expect when not using a local port

Last edited by Wims; 11/12/10 08:49 AM.

#mircscripting @ irc.swiftirc.net == the best mIRC help channel
Joined: Feb 2006
Posts: 546
J
Fjord artisan
Offline
Fjord artisan
J
Joined: Feb 2006
Posts: 546
the event triggers twice because you didn't read all the data from the receive buffer the first time: $sock().rq would probably reveal that there is more information to read. i say 'probably' because if you try this test many times, you're bound to get 'correct' results at least some of the time.

as drum said, it's just the way UDP works :P you can't guarantee that the data in the receive buffer came from multiple sources at the mIRC level, and i don't think it can be done seamlessly at the C level either (if we assume that the application polls the system for new incoming data - the two datagrams you send may be received by the system in between polls).

you could have the clients encode all details that could potentially be lost as the first few bytes of every message.


"The only excuse for making a useless script is that one admires it intensely" - Oscar Wilde
Joined: Oct 2003
Posts: 3,918
A
Hoopy frood
Offline
Hoopy frood
A
Joined: Oct 2003
Posts: 3,918
This is actually a regression.

I see the correct separate source port numbers when running the code in mIRC 6.35.

If there's a design limitation in the way mIRC reads sockets, it's been introduced recently, and I can confirm that this is a bug-- a fairly serious one, since it means a script cannot properly respond to a client if multiple clients send data at the same time. Data can leak from one connection to another-- especially since this bug likely applies to the source address too. Again: this bug means that a malicious user could easily steal response packets from any mIRC UDP server script simply by flooding it with garbage packets while other clients interact with the server normally.

For reference, wireshark shows the following:

7 219.155133 192.168.0.2 192.168.0.2 UDP Source port: 65050 Destination port: vcom-tunnel
8 219.155320 192.168.0.2 192.168.0.2 UDP Source port: 65051 Destination port: vcom-tunnel

mIRC *is* actually sending the packets on separate ports. The problem is indeed that the sockread event is tripping over itself.


- argv[0] on EFnet #mIRC
- "Life is a pointer to an integer without a cast"
Joined: Dec 2002
Posts: 344
D
Pan-dimensional mouse
Offline
Pan-dimensional mouse
D
Joined: Dec 2002
Posts: 344
Originally Posted By: argv0
Again: this bug means that a malicious user could easily steal response packets from any mIRC UDP server script simply by flooding it with garbage packets while other clients interact with the server normally.


I doubt it could be done "easily," but it certainly sounds like it could be exploited. The reason these UDP packets arrive so closely together is because the source and destination are the same machine for both packets. Introducing any amount of latency through a network would probably make it considerably harder to replicate but it definitely is possible.

Honestly, I think the way mIRC relies on a $sock() call to retrieve things like the remote IP and port is flawed from the beginning. Instead of simply trying to patch this bug (especially if it ends up being a convoluted bug to fix), I think it would be far more appropriate if mIRC delivered the information directly to the event as locally-scoped identifiers. For example, a UDP packet's source IP ought to be passed to the event in the form of something like $saddr instead of having to use $sock($sockname).saddr. I'd like to see this extended to all of the relevant $sock() calls (for TCP connections, too), but at the very least, to .saddr and .sport.

Joined: Jul 2006
Posts: 4,149
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,149
Quote:
the event triggers twice because you didn't read all the data from the receive buffer the first time: $sock().rq would probably reveal that there is more information to read. i say 'probably' because if you try this test many times, you're bound to get 'correct' results at least some of the time.
I did read all the data, try it because I did test this many times, and the bug occurs 100% of the time.
Quote:
as drum said, it's just the way UDP works :P you can't guarantee that the data in the receive buffer came from multiple sources at the mIRC level, and i don't think it can be done seamlessly at the C level either (if we assume that the application polls the system for new incoming data - the two datagrams you send may be received by the system in between polls).
No it's not how udp work, thanks god in C you can accuratly get the source ip and port.

Quote:
you could have the clients encode all details that could potentially be lost as the first few bytes of every message.
??

Originally Posted By: argv0
mIRC *is* actually sending the packets on separate ports. The problem is indeed that the sockread event is tripping over itself.
Yep, must be related


#mircscripting @ irc.swiftirc.net == the best mIRC help channel
Joined: Feb 2006
Posts: 546
J
Fjord artisan
Offline
Fjord artisan
J
Joined: Feb 2006
Posts: 546
Originally Posted By: Wims
I did read all the data, try it because I did test this many times, and the bug occurs 100% of the time.


you used /sockread -n which will only read the first line from the receive buffer. try this:

Code:
on *:udpread:test:{
  echo -a * $sock(test).rq
  sockread &a
}


if you repeat your tests you will probably notice, as i did, that the event mostly triggers only once with $sock().rq = 10. this implies that both lines are being put into the receive buffer before on UDPREAD has a chance to trigger, and $sock().sport will quite rightly return the address of the sender of the last received datagram.

the issue is why mIRC isn't triggering on UDPREAD fast enough for the scripter to be able to distinguish between two distinct datagrams from two different senders.

Originally Posted By: Wims
No it's not how udp work, thanks god in C you can accuratly get the source ip and port.


i've never used UDP in C so i referred to the Berkeley socket API (on which Winsock is modeled) as in another thread concerning accept() with TCP sockets. and, like that issue, it was revealed that the limitation was inherent to the API rather than mIRC's own implementation:

http://en.wikipedia.org/wiki/Berkely_sockets#Server_2

the infinite loop at the end shows that the application is polling the system. i'm not sure if it's sensible to assume that multiple datagrams could be received in between polls - notice how the article prefaces its example with a word of caution as to its reliability in various ways - but it would certainly explain mIRC's behaviour. EDIT: disregard, this is nonsense

Originally Posted By: Wims
Originally Posted By: jaytea
you could have the clients encode all details that could potentially be lost as the first few bytes of every message.
??


if destination IP address and port is important and is being lost frequently then encode them into the message. a naive way to do this would be to use the first 4 bytes for the IP and the next 2 for the port.


"The only excuse for making a useless script is that one admires it intensely" - Oscar Wilde
Joined: Oct 2003
Posts: 3,918
A
Hoopy frood
Offline
Hoopy frood
A
Joined: Oct 2003
Posts: 3,918
Originally Posted By: drum
The reason these UDP packets arrive so closely together is because the source and destination are the same machine for both packets.


Not really. 2 independent packets can arrive closely together in any order-- the fact that there's no latency locally is irrelevant, because it doesn't matter how long it takes the packet to arrive, it only matters how long mIRC takes to *read* the packet queue. If you saturate the receive queue of a machine so heavily that the vast majority of the packets are yours, eventually you will get such a collision-- it's basic statistics. Even if only 1 of every 100 packets got hijacked, that would be significant after a very short amount of time. Also realize that in a real world scenario, both clients would be remote, so the local thing doesn't apply anyway. It would be fairly easy to write a proof of concept for something like this.

Originally Posted By: drum
Honestly, I think the way mIRC relies on a $sock() call to retrieve things like the remote IP and port is flawed from the beginning.


Nothing is flawed, because it worked fine before. This is nothing but a regression. The design of the system is fine, and there's no need to break backwards compatibility and change functionality now. In any case, there's also no reason to add bloat by creating extra event-specific identifiers when a socket-specific identifier makes perfect sense in a single-threaded environment. In a single-threaded environment, per-socket data is *equivalent* to locally-scoped data.



- argv[0] on EFnet #mIRC
- "Life is a pointer to an integer without a cast"
Joined: Jul 2006
Posts: 4,149
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,149
Ah, I see your point now, maybe it could be improved to speed up the triggering part


#mircscripting @ irc.swiftirc.net == the best mIRC help channel
Joined: Apr 2004
Posts: 871
Sat Offline
Hoopy frood
Offline
Hoopy frood
Joined: Apr 2004
Posts: 871
Originally Posted By: jaytea
if you repeat your tests you will probably notice, as i did, that the event mostly triggers only once with $sock().rq = 10. this implies that both lines are being put into the receive buffer before on UDPREAD has a chance to trigger, and $sock().sport will quite rightly return the address of the sender of the last received datagram.

the issue is why mIRC isn't triggering on UDPREAD fast enough for the scripter to be able to distinguish between two distinct datagrams from two different senders.

You have a fundamental misunderstanding about how UDP works, and it looks like mIRC now gets it wrong, too.

UDP is a datagram-oriented protocol. Datagrams are individual units, each with their own source IP+port, destination IP+port, and payload. As far as the application can see, they are never combined or split up. Hence, "on UDPREAD" should trigger once per datagram, and it is then up to the script to process as much as it wants from that datagram. All the while, $sock().saddr/.sport should return the source IP and port of that diagram. If two datagrams are received, "on UDPREAD" should trigger twice.

Originally Posted By: jaytea
i'm not sure if it's sensible to assume that multiple datagrams could be received in between polls - notice how the article prefaces its example with a word of caution as to its reliability in various ways - but it would certainly explain mIRC's behaviour.

Each recvfrom() call will return up to one whole datagram, no matter how many datagrams arrived before it was called. The word of caution relates mostly to the behavior of the underlying network, not to the behavior of the operating system; the only possible concern is that if the application is not fast enough in processing the incoming datagrams, the operating system may decide to throw away some of them. None of that is relevant to this problem.

Yes, this is a real bug.


Saturn, QuakeNet staff
Joined: Dec 2002
Posts: 344
D
Pan-dimensional mouse
Offline
Pan-dimensional mouse
D
Joined: Dec 2002
Posts: 344
Originally Posted By: argv0
Nothing is flawed, because it worked fine before. This is nothing but a regression. The design of the system is fine, and there's no need to break backwards compatibility and change functionality now. In any case, there's also no reason to add bloat by creating extra event-specific identifiers when a socket-specific identifier makes perfect sense in a single-threaded environment. In a single-threaded environment, per-socket data is *equivalent* to locally-scoped data.


When I say it is "flawed," I mean that it is more complicated than it needs to be. I also think it is confusing. After some more thought, I think I have reversed my opinion on the TCP-related calls. It makes sense that the remote IP and port aren't going to change during the course of a TCP connection's lifespan, so $sock().ip and $sock().port seem like reasonable ways to deal with TCP sockets.

However, the issue with UDP sockets is that each datagram will have a potentially different remote IP/port associated with it. In this manner, the $sock().sport and $sock().saddr information isn't actually tied to that socket name (since you can receive datagrams from multiple sources on that same socket name); instead, that data is tied to the individual datagram. This is why I think it makes more sense as a locally-scoped identifier for the UDPREAD event.

Ultimately, I think $udpaddr and $udpport from within the scope of ON UDPREAD make more sense in that it emphasizes that this information is only valid for that particular datagram and not for the socket in general. It may not matter in terms of functionality but it does seem like a more LOGICAL way to design it. It also seems possible (although I can only guess because we don't know the true cause) that the bug being discussed in this thread might not occur if the IP/port information was passed directly to the event instead.

Along the same lines, I'd like to see something like $sockip and $sockport in ON SOCKLISTEN events, to be able to access the remote IP/port that is connecting before accepting the connection.

Joined: Oct 2004
Posts: 8,330
Hoopy frood
Offline
Hoopy frood
Joined: Oct 2004
Posts: 8,330
Originally Posted By: drum
Along the same lines, I'd like to see something like $sockip and $sockport in ON SOCKLISTEN events, to be able to access the remote IP/port that is connecting before accepting the connection.


This was discussed in a feature request thread a few weeks back. I don't claim to really understand everything said in that thread, but it was explained that this is not possible. Basically, you don't receive that information until the connection is made. You can probably find the thread in the first page or two of the feature request forum.


Invision Support
#Invision on irc.irchighway.net
Joined: Dec 2002
Posts: 3,138
C
Hoopy frood
Offline
Hoopy frood
C
Joined: Dec 2002
Posts: 3,138
Originally Posted By: Riamus2
This was discussed in a feature request thread a few weeks back. I don't claim to really understand everything said in that thread, but it was explained that this is not possible. Basically, you don't receive that information until the connection is made. You can probably find the thread in the first page or two of the feature request forum.

It is possible to access the information via the 'conditional accept' mechanism, which was pointed out in the thread you're talking about. There appear to be some drawbacks to this though, at least on Vista upwards.

Joined: Oct 2003
Posts: 3,918
A
Hoopy frood
Offline
Hoopy frood
A
Joined: Oct 2003
Posts: 3,918
I understand what your view is, and for the most part I agree-- but I still don't think there's any reason to introduce a new API when there is an existing one that works fine. Just like many other features in mIRC's language, you tend to have to abandon the "conceptually ideal" solution for the "it works" solution.

For reference, the reason $sock().saddr/sport were used were likely for consistency's sake. Introducing a $udpaddr and $udpport would be *more* confusing, since there would be no related $tcpaddr and $tcpport for the TCP events. On the flipside, most scripters are looking to $sock() to get information from the socket, and there is no precedence for event specific identifiers in SOCK* events. It would be confusing as well, in this sense, to introduce event specific identifiers for one thing when virtually everything else is tied to $sock(). IMO it makes sense here to break the conceptually clean design in order to maintain consistency.



- argv[0] on EFnet #mIRC
- "Life is a pointer to an integer without a cast"
Joined: Jul 2006
Posts: 4,149
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,149
After some test, there is another problem, sometime the on udpread event (still) doesn't show any source ip or source port.


#mircscripting @ irc.swiftirc.net == the best mIRC help channel
Joined: Dec 2002
Posts: 5,420
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 5,420
Thanks I was able to reproduce the issue with saddr/sport not being correct in situations where multiple /sockudp commands are issued at the same time. This has been fixed for the next version.

Joined: Jul 2006
Posts: 4,149
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,149
Thanks


#mircscripting @ irc.swiftirc.net == the best mIRC help channel

Link Copied to Clipboard