zoukankan      html  css  js  c++  java
  • Delphi Sockets.pas单元中TIpSocket的Bug

      由于最近在看UDP打洞,本着力求精简,不用三方控件的原则下,折腾了Sockets.pas这个单元。在弄清如何用Sockets单元中类执行udp server的功能时,发现了一个小BUG。

    BUG描述:

      无法判断UDP数据包是谁发来的。这个BUG存在于TIpSocket类的ReceiveFrom方法中,功能是从当前的Socket中接收数据包,并可以返回对方的TSockAddr,在UDP接收中,这个很重要,因为UDP需要知道数据包是从哪里发送来的。

      此函数的实现为:

    function TIpSocket.ReceiveFrom(var buf; bufsize: Integer; ToAddr: TSockAddr; var len: Integer; flags: Integer): Integer;
    begin
    {$IFDEF MSWINDOWS}
    Result := ErrorCheck(WinSock.recvfrom(FSocket, buf, bufsize, flags, ToAddr, len));
    {$ENDIF}
    {$IFDEF LINUX}
    Result := ErrorCheck(Libc.recvfrom(FSocket, buf, bufsize, flags, @ToAddr, @len));
    {$ENDIF}
    if Result <> SOCKET_ERROR then
    DoReceive(pchar(@Buf), Result);
    end;

    可以发现,ToAddr是传值进入函数而被WinSock.RecvFrom调用的,这意味着,返回的源地址并不能返回。修正起来很简单,把声明改成var传入就可以了

    function TIpSocket.ReceiveFrom(var buf; bufsize: Integer; var ToAddr: TSockAddr; var len: Integer; flags: Integer): Integer;
    begin
    {$IFDEF MSWINDOWS}
    Result := ErrorCheck(WinSock.recvfrom(FSocket, buf, bufsize, flags, ToAddr, len));
    {$ENDIF}
    {$IFDEF LINUX}
    Result := ErrorCheck(Libc.recvfrom(FSocket, buf, bufsize, flags, @ToAddr, @len));
    {$ENDIF}
    if Result <> SOCKET_ERROR then
    DoReceive(pchar(@Buf), Result);
    end;

    修正方案:
    使用给vcl打补丁的方法

    unit u_SocketsPatcher;

    interface
    uses
    Sockets, WinSock;

    implementation
    Procedure PatchVCLCode(ProcOld, ProcNew: Pointer);
    var
    newCode : packed record
    JmpRel32 : Byte;
    Offset32 : Integer;
    end;
    begin
    newCode.JmpRel32 := $E9;
    newCode.Offset32 := Integer(procNew) - Integer(procOld) - 5;
    WriteProcessMemory(
    GetCurrentProcess,
    procOld,
    @newCode,
    SizeOf(newCode),
    DWORD(nil^) );

    end;

    type
    TIPSocketPatch = Class(TIpSocket)
    Private
    function ReceiveFrom2(var buf; bufsize: Integer; var ToAddr: TSockAddr; var len: Integer; flags: Integer = 0): Integer;
    End;

    { TIPSocketPatch }

    function TIPSocketPatch.ReceiveFrom2(var buf; bufsize: Integer;
    var ToAddr: TSockAddr; var len: Integer; flags: Integer): Integer;
    begin
    {$IFDEF MSWINDOWS}
    Result := ErrorCheck(WinSock.recvfrom(Handle, buf, bufsize, flags, ToAddr, len));
    {$ENDIF}
    {$IFDEF LINUX}
    Result := ErrorCheck(Libc.recvfrom(FSocket, buf, bufsize, flags, @ToAddr, @len));
    {$ENDIF}
    if Result <> SOCKET_ERROR then
    DoReceive(pchar(@Buf), Result);
    end;

    initialization
    PatchVCLCode(@TIpSocket.ReceiveFrom, @TIPSocketPatch.ReceiveFrom2);
    end.

    在工程中加入此单元的引用即可

    BUG的影响

      由于DELPHIER们使用UDP很少使用Sockets.pas中的单元而一般用Indy组件,所以这个BUG影响范围很小,即使使用udp,也都是只用TUdpSocet类作为Client端,而不会调用这个方法。所以,这是一个基本上不会被调用的BUG函数,也很少影响到Delphier们。但如果要想自己力求精简的编写一些基础组件的话,这个问题就会被波及,比如此次我弄的这个UDP Hole Punching的时候,终于显现出来了,也花了我半天的时间,唉....
     

        

  • 相关阅读:
    CodeForces 604D 【离散数学 置换群】
    CodeForces 604C 【思维水题】`
    CodeForces 602E【概率DP】【树状数组优化】
    CodeForces 602D 【单调队列】【简单数学】
    HDU 3535 【背包】
    CodeForces 593D【树链剖分】
    HYSBZ 1036 【树链剖分】
    POJ 2352 【树状数组】
    POJ 2182【树状数组】
    机器学习实战笔记-2-7分类机器学习形象化总结
  • 原文地址:https://www.cnblogs.com/littlestone08/p/2296015.html
Copyright © 2011-2022 走看看