| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 1463 人关注过本帖
标题:如何改成可以搜索多IP,例如扫描192.168.0.0到192.168.10.10之间的端口,怎 ...
只看楼主 加入收藏
ldtiger
Rank: 1
等 级:新手上路
帖 子:6
专家分:0
注 册:2010-11-18
结帖率:100%
收藏
 问题点数:0 回复次数:0 
如何改成可以搜索多IP,例如扫描192.168.0.0到192.168.10.10之间的端口,怎么加循环
PortScan.rar (84.76 KB)

program Portscan;
(*
  1.10d - Okay, upon request the whole file moves out of the EXE again to be
          able to customize the port descriptions ;)
  1.10c - now the programmer can optionally include the short names for the
          ports as string table resource using the PORTNAMES compiler switch.
          (A Perl script in the \SOURCE directory can be used to extract the
           names. Just put a file called "port-numbers.txt" into the directory
           and then execute the .CMD file to extract the names - get the file
           from: http://www.)
  1.10b - can be minimized to tray now by double clicking the caption
  1.10a - small enhancements
  1.10  - sports new scheduling scheme. Thanks to Hagen for pointing it out to me.
  1.00  - initial release

  The program can definitely be compiled with Delphi 3 through 5!

  The program will start and maybe run on Windows 9x. But when reaching a limit
  of 32K items in the listbox it will hang or whatever. I don't know. I just
  want you to notice it. It's a limitation of Windows 9x I am not willing to
  work around, since Windows 9x hopefully will disappear from PCs soon.
*)
uses
  Windows,
  Messages,
  ShellApi,
  CommCtrl,
  CommDlg,
  WinSock;

{$R MAIN.RES}
{$INCLUDE .\Include\FormatString.pas}
{$INCLUDE .\Include\GetFont.pas}

const
  IDD_DIALOG1 = 101;
  IDI_ICON1 = 102;
  IDI_ICON2 = 103;
  IDC_EDITIP = 1001;
  IDC_EDITPORT_FROM = 1005;
  IDC_EDITPORT_TO = 1008;
  IDC_LIST1 = 1009;
  IDC_BUTTON_OPENPORTS = 1010;
  IDC_BUTTON_SAVE = 1011;
  IDC_CHECK1 = 1012;
  IDC_EDIT_NUMTHREADS = 1014;
  IDC_SPIN_NUMTHREADS = 1015;
  IDC_STATIC_NUMTHREADS = 1016;
  IDC_STATIC_NUMITEMS = 1017;

// Some application defined window messages
  WM_ADDIP_TCPCONNPORT = WM_APP + 123; // open TCP port
  WM_ADDIP_TCPPORT = WM_APP + 124; // closed TCP port
  WM_ADDIP_UDPCONNPORT = WM_APP + 125; // open UDP port
  WM_ADDIP_UDPPORT = WM_APP + 126; // closed UDP port
  WM_SETCOUNTER = WM_APP + 200; // sets the thread counter STATIC
  WM_SHELLNOTIFY = WM_APP + 201;
  SC_TRAY = WM_APP + 202;

  maxports = $FFFF;
  cancelports = 1 + maxports * 2;
  allfiles_filterstring = 'Text files'#0'*.txt'#0'All files'#0'*.*'#0#0;
  portfile = 'ports.conf';

type
  PIP = ^TIP;
  TIP = record
    IP,
      StartPort,
      EndPort: DWORD;
    ThreadCounter: Integer;
  end;

  PPortArray = ^TPortArray;
  TPortArray = array[0..$FFFF] of string;

var
  portdesc: PPortArray;
  note: TNotifyIconData;
  numthreads: DWORD = $100; // FIXED!!!
  wsa: TWSAData;
  cs: TRTLCriticalSection;
  hDlg: HWND = 0;
  ct: Boolean = False; // Icon "counter"
  icons: array[0..1] of HICON; // Two alternating icons
  IP: TIP;
  gPortCounter: DWORD;
  MsgTaskbarcreated: DWORD = 0;

function EnumChildSetFonts(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
(*
  Iterates through all dialog child controls and sets fonts and text length limit.
*)
begin
  result := True;
// For any of the controls here, let's set the font :)
  case GetDlgCtrlID(hwnd) of
    IDC_LIST1,
      IDC_EDITIP,
      IDC_EDITPORT_FROM,
      IDC_EDITPORT_TO:
      SendMessage(hwnd, WM_SETFONT, lParam, ord(TRUE));
  end;
  case GetDlgCtrlID(hwnd) of
    IDC_LIST1,
      IDC_EDITIP:
      SendMessage(hwnd, EM_SETLIMITTEXT, 20, 0);
    IDC_EDITPORT_FROM,
      IDC_EDITPORT_TO:
      SendMessage(hwnd, EM_SETLIMITTEXT, 5, 0);
  end;
end;

function GetIP(hwnd: HWND): DWORD;
//  Get's an IP address in host byte order from the text of the IDC_EDITIP control
var
  IP: DWORD;
  pc: PChar;
begin
  result := 0;
  IP := GetWindowTextLength(GetDlgItem(hwnd, IDC_EDITIP)) + 2;
  GetMem(pc, IP);
  if Assigned(pc) then
  try
    ZeroMemory(pc, IP);
    GetWindowText(GetDlgItem(hwnd, IDC_EDITIP), pc, IP);
    result := ntohl(inet_addr(pc));
  finally
    FreeMem(pc);
  end;
end;

function ScanTCPPort(IP, Port: DWORD): Boolean;
//  Scans a specific TCP port and on a specific IP
var
  OPTON: DWORD;
  TCPsock: TSocket;
  A: TSockAddrIn;
begin
  result := False;
// Create/open a socket (stream, not datagram)
  TCPsock := socket(AF_INET, SOCK_STREAM, 0);
  if TCPsock <> INVALID_SOCKET then
  try
// Set socket options
    OPTON := 0;
    setsockopt(TCPsock, SOL_SOCKET, SO_KEEPALIVE, @OPTON, sizeof(OPTON));
    OPTON := 1;
    setsockopt(TCPsock, SOL_SOCKET, SO_DONTLINGER, @OPTON, sizeof(OPTON));
// Only for compatibility with WinSock 1.1! It's default on newer versions.
    setsockopt(TCPsock, IPPROTO_TCP, TCP_NODELAY, @OPTON, sizeof(OPTON));
// Reset and init address structure
    ZeroMemory(@A, sizeof(A));
    A.sin_family := AF_INET;
    A.sin_addr.S_addr := ntohl(IP);
    A.sin_port := htons(Port);
// Try to connect and return result
    result := connect(TCPsock, A, sizeof(A)) = 0;
  finally
// Close the socket
    closesocket(TCPsock);
  end;
end;

function BeepThread(unused: DWORD): DWORD; stdcall;
// Does a long beep
begin
  result := 0;
  Windows.Beep(200, 100);
  Windows.Beep(400, 100);
  Windows.Beep(600, 100);
  Windows.Beep(800, 100);
  Windows.Beep(1000, 100);
  Windows.Beep(1200, 100);
  Windows.Beep(1400, 100);
  Windows.Beep(1600, 100);
  Windows.Beep(1800, 100);
  Windows.Beep(2000, 100);
end;

function ScanThread(unused: DWORD): DWORD; stdcall;
// Does the job for ONE port each time. It loops in a loop that increases a global
// port counter. The next unscanned port is scanned by the next free thread
var
  lPort: DWORD;
begin
  result := 0;
// Thread counter
  InterLockedIncrement(IP.ThreadCounter);
// Post updated number of threads
  PostMessage(hDlg, WM_SETCOUNTER, IP.ThreadCounter, 0);
// Set lower thread priority
  SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_IDLE);
// Loop while the current port number is smaller or equal the end port number
  while True do
  begin
    asm
// Atomic operation
        MOV   EAX, 1
        XADD  gPortCounter, EAX
        MOV   lPort, EAX
    end;
// Exit condition
    if lPort > IP.EndPort then
      Break;
// Scan ports ...
    if ScanTCPPort(IP.IP, lPort) then
      PostMessage(hDlg, WM_ADDIP_TCPCONNPORT, IP.IP, lPort)
    else
      PostMessage(hDlg, WM_ADDIP_TCPPORT, IP.IP, lPort);
  end;
// Atomic operation ... decreases thread counter
  InterLockedDecrement(IP.ThreadCounter);
// Post updated number of threads
  PostMessage(hDlg, WM_SETCOUNTER, IP.ThreadCounter, 0);
end;

procedure TryPortscan(hwnd: HWND);
// Retrieves all necessary values from the dialog and starts scanning.
var
  i, TID: DWORD;
  trans: BOOL;
begin
  IP.IP := GetIP(hwnd);
  if (IP.IP <> INADDR_NONE) then
  begin
    SetDlgItemInt(hwnd, IDC_STATIC_NUMITEMS, 0, False);
    i := SendDlgItemMessage(hwnd, IDC_SPIN_NUMTHREADS, UDM_GETPOS, 0, 0);
    IP.StartPort := GetDlgItemInt(hwnd, IDC_EDITPORT_FROM, trans, False);
    IP.EndPort := GetDlgItemInt(hwnd, IDC_EDITPORT_TO, trans, False);
// If the ports have wrong order (max < min) then change them
    if IP.StartPort > IP.EndPort then
    begin
      TID := IP.StartPort;
      IP.StartPort := IP.EndPort;
      IP.EndPort := TID;
      if IP.EndPort > maxports then
        SetDlgItemInt(hwnd, IDC_EDITPORT_FROM, IP.StartPort, False);
      SetDlgItemInt(hwnd, IDC_EDITPORT_TO, IP.EndPort, False);
    end;
// Prevent thread switching
    if i > (IP.EndPort - IP.StartPort) then
      i := (IP.EndPort - IP.StartPort) + 1;
    SendDlgItemMessage(hwnd, IDC_LIST1, LB_RESETCONTENT, 0, 0);
    gPortCounter := IP.StartPort;
    IP.ThreadCounter := 0;
// Set lower thread priority
    SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_BELOW_NORMAL);
// Start a whole lott of threads ...
    for i := 0 to i - 1 do
// Parameter is the current counter value
      CreateThread(nil, $FFFF, @ScanThread, nil, 0, TID);
  end
  else
    MessageBox(hwnd, 'Please give a valid IP address and number of threads!', nil, 0);
end;

procedure EnableWindows(hwnd: HWND; b: BOOL);
// Enables or disables controls that might disturb while scanning
begin
  EnableWindow(GetDlgItem(hwnd, IDOK), b);
  EnableWindow(GetDlgItem(hwnd, IDC_EDIT_NUMTHREADS), b);
  EnableWindow(GetDlgItem(hwnd, IDC_EDITIP), b);
  EnableWindow(GetDlgItem(hwnd, IDC_EDITPORT_FROM), b);
  EnableWindow(GetDlgItem(hwnd, IDC_EDITPORT_TO), b);
  EnableWindow(GetDlgItem(hwnd, IDC_SPIN_NUMTHREADS), b);
//  EnableWindow(GetDlgItem(hwnd, IDC_BUTTON_SAVE), b);
end;

procedure SetIcon(hwnd: HWND; handle: HICON);
// Sets the main window's icon
begin
  SendMessage(hwnd, WM_SETICON, ICON_BIG, handle);
  SendMessage(hwnd, WM_SETICON, ICON_SMALL, handle);
end;

function GetFileName(hwnd: HWND; default: string; var returnname: string): Boolean;
// Gets the name for the file to save the report
var
  ofn: TOpenFilename;
  ofn_buffer: array[0..MAX_PATH] of Char;
begin
  ZeroMemory(@ofn_buffer, sizeof(ofn_buffer));
  ZeroMemory(@ofn, sizeof(ofn));
  lstrcpyn(@ofn_buffer[0], @default[1], sizeof(ofn_buffer));
  ofn.lStructSize := sizeof(ofn);
  ofn.lpstrFile := @ofn_buffer;
  ofn.hWndOwner := hwnd;
  ofn.hInstance := hInstance;
  ofn.lpstrFilter := @allfiles_filterstring[1];
  ofn.nMaxFile := sizeof(ofn_buffer);
  ofn.Flags := OFN_PATHMUSTEXIST or OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY;
  result := GetSaveFileName(ofn);
  if result then
    SetString(returnname, PChar(@ofn_buffer), lstrlen(@ofn_buffer[0]));
end;

function GetLBItem(hwnd: HWND; idx: Integer): string;
// Gets one listbox item in a string (by index)
var
  i: Integer;
  pc: PChar;
begin
  result := '';
  i := SendMessage(hwnd, LB_GETTEXTLEN, idx, 0) + 2;
  GetMem(pc, i);
  if Assigned(pc) then
  try
    ZeroMemory(pc, i);
    SendMessage(hwnd, LB_GETTEXT, idx, LPARAM(pc));
    SetString(result, pc, lstrlen(pc));
  finally
    FreeMem(pc);
  end;
end;

procedure CreateTrayIcon(hwnd: HWND);
// Creates the tray icon
begin
  note.cbSize := SizeOf(TNotifyIconData);
  note.wnd := hwnd;
  note.uID := SC_TRAY;
  note.uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
  note.uCallbackMessage := WM_SHELLNOTIFY;
  note.hIcon := icons[0];
  lstrcpy(note.szTip, 'FastScan (double click restores)');
  ShowWindow(hWnd, SW_HIDE);
  Shell_NotifyIcon(NIM_ADD, @note);
end;

procedure DestroyTrayIcon;
// Destroys the tray icon
begin
  Shell_NotifyIcon(NIM_DELETE, @note);
end;

procedure ModifyTrayIconText;
// Modifies the tray icon
begin
  Shell_NotifyIcon(NIM_MODIFY, @note);
end;

procedure BlinkTrayIcon;
// Actually blinks the tray icon
begin
  note.hIcon := icons[ord(ct)];
  ModifyTrayIconText;
end;

procedure TimerFunc(hwnd: HWND; uMsg: UINT; idEvent: UINT; dwTime: DWORD); stdcall;
// Makes the tray icon blink when finished
begin
  ct := not ct;
  BlinkTrayIcon;
end;

function GetTCPPortDescription(IP: DWORD; Port: Word; ports: PPortArray): string;
begin
  result := Format('%3d.%3d.%3d.%3d:%5d [TCP] open', [
    Byte(IP shr 24),
      Byte(IP shr 16),
      Byte(IP shr 8),
      Byte(IP),
      Port]);
  if Assigned(ports) then
    if ports^[Port] <> '' then
      result := Format('%s -> %s', [result, ports^[Port]]);
end;

function DlgFunc(hwnd: HWND; umsg: UINT; wparam: WPARAM; lparam: LPARAM): BOOL; stdcall;
var
  s: string;
  hFile: THandle;
  written: DWORD;
  i: Integer;
  se: servent;
  pse: PServEnt absolute se;
  x,y:integer;
begin
  Result := True;
  case umsg of
    WM_INITDIALOG:

      begin
        hDlg := hwnd;
        SetIcon(hwnd, icons[0]);
// Set fixed font for all edit fields
        EnumChildWindows(hwnd, @EnumChildSetFonts, GetFont(hwnd, 8, FW_NORMAL, True));
// Set initial IP address

        begin
        for x:=52 to 54 do
          for y:=77 to 79 do
        SetDlgItemText(hwnd, IDC_EDITIP, '172.26.x.y');
        end;
        SendDlgItemMessage(hwnd, IDC_SPIN_NUMTHREADS, UDM_SETRANGE, 0, MAKELONG(numthreads, 1));
        SendDlgItemMessage(hwnd, IDC_SPIN_NUMTHREADS, UDM_SETPOS, 0, MAKELONG(numthreads, 0));
// Set initial port range
        SetDlgItemInt(hwnd, IDC_EDITPORT_FROM, 0, False);
        SetDlgItemInt(hwnd, IDC_EDITPORT_TO, maxports, False);
        SetDlgItemInt(hwnd, IDC_STATIC_NUMTHREADS, 0, False);
        SetDlgItemInt(hwnd, IDC_STATIC_NUMITEMS, 0, False);
// Register the message
        MsgTaskbarcreated := RegisterWindowMessage('TaskbarCreated');
      end;

    WM_TIMER:
      case wParam of
        IDD_DIALOG1:
          begin
            ct := not ct;
            SetIcon(hwnd, icons[ord(ct)]);
          end;
      end;
    WM_SETCOUNTER:
      begin
        SetDlgItemInt(hwnd, IDC_STATIC_NUMTHREADS, wParam, False);
        if wParam > 0 then
          EnableWindows(hwnd, False)
        else
        begin
          EnableWindows(hwnd, True);
// Remove timer
          KillTimer(hwnd, IDD_DIALOG1);
// Reset icon
          SetIcon(hwnd, icons[0]);
// Set lower thread priority
          SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_NORMAL);
          if not IsWindowVisible(hwnd) then
          begin
// Make the tray icon blink
            SetTimer(hwnd, SC_TRAY, 350, @TimerFunc);
// Make a long beeeeep ;)
            CreateThread(nil, $FFFF, @BeepThread, nil, 0, PDWORD(nil)^);
          end;
        end;
      end;
    WM_ADDIP_TCPCONNPORT:
      begin
//        s := Format('%3.3d.%3.3d.%3.3d.%3.3d:%5.5d - ', [
        s := GetTCPPortDescription(wParam, lParam, portdesc);
        SendDlgItemMessage(hwnd, IDC_LIST1, LB_ADDSTRING, 0, Integer(@s[1]));
        if IsDlgButtonChecked(hwnd, IDC_CHECK1) = BST_CHECKED then
          SendDlgItemMessage(hwnd, IDC_LIST1, LB_SETTOPINDEX,
            SendDlgItemMessage(hwnd, IDC_LIST1, LB_GETCOUNT, 0, 0) - 1, 0);
        SetDlgItemInt(hwnd, IDC_STATIC_NUMITEMS, gPortCounter, False);
      end;
    WM_ADDIP_TCPPORT:
      SetDlgItemInt(hwnd, IDC_STATIC_NUMITEMS, gPortCounter, False);
    WM_CTLCOLOREDIT:
      case GetDlgCtrlID(lParam) of
        IDC_EDITIP:
          begin
            SetTextColor(wParam, RGB($00, $80, $00));
            SetBkColor(wParam, GetSysColor(COLOR_INFOBK));
            result := BOOL(GetSysColorBrush(COLOR_INFOBK));
          end;
        IDC_EDITPORT_FROM,
          IDC_EDITPORT_TO:
          begin
            SetTextColor(wParam, RGB($00, $00, $80));
            SetBkColor(wParam, GetSysColor(COLOR_INFOBK));
            result := BOOL(GetSysColorBrush(COLOR_INFOBK));
          end;
        IDC_EDIT_NUMTHREADS:
          begin
            SetTextColor(wParam, RGB($80, $00, $00));
            SetBkColor(wParam, GetSysColor(COLOR_INFOBK));
            result := BOOL(GetSysColorBrush(COLOR_INFOBK));
          end;
      else
        result := False;
      end;
    WM_CTLCOLORLISTBOX:
      case GetDlgCtrlID(lParam) of
        IDC_LIST1:
          begin
            SetTextColor(wParam, RGB($00, $00, $80));
            SetBkColor(wParam, GetSysColor(COLOR_BTNFACE));
            result := BOOL(GetSysColorBrush(COLOR_BTNFACE));
          end;
      else
        result := False;
      end;
    WM_CLOSE:
      EndDialog(hwnd, 0);
    WM_COMMAND:
      if HiWord(WParam) = BN_CLICKED then
        case LOWORD(wParam) of
          IDOK:
            begin
              SetTimer(hwnd, IDD_DIALOG1, 400, nil);
              TryPortScan(hwnd);
            end;
          IDCANCEL:
            SendMessage(hwnd, WM_CLOSE, 0, 0);
          IDC_BUTTON_OPENPORTS:
// Open the IANA website with port number assignments
            ShellExecute(hwnd,
              'open',
              'http://www.',
              nil,
              nil,
              SW_SHOW);
          IDC_BUTTON_SAVE:
            begin
              i := SendDlgItemMessage(hwnd, IDC_LIST1, LB_GETCOUNT, 0, 0);
              if i > 0 then
                if GetFileName(hwnd, 'ScanReport.txt', s) then
                begin
                  hFile := CreateFile(@s[1], GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
                  if ((hFile <> INVALID_HANDLE_VALUE) and (i <> LB_ERR)) then
                  try
                    for i := 0 to i do
                    begin
                      s := GetLBItem(GetDlgItem(hwnd, IDC_LIST1), i) + #13#10;
                      WriteFile(hFile, PChar(@s[1])^, length(s), written, nil);
                    end;
                  finally
                    CloseHandle(hFile);
                  end
                  else
                    MessageBox(hwnd, 'Could not create file.', nil, 0);
                end;
            end;
        end;
(******************************************************************************
Handle: minimize to tray
******************************************************************************)
    WM_SIZE:
      if wParam = SIZE_MINIMIZED then
        CreateTrayIcon(hwnd);
    WM_SHELLNOTIFY:
      case wParam of
        SC_TRAY:
          case lParam of
            WM_LBUTTONDBLCLK:
              begin
                DestroyTrayIcon;
                ShowWindow(hWnd, SW_RESTORE);
                SetForeGroundWindow(hwnd);
              end;
          end;
      end;
    WM_NCLBUTTONDBLCLK:
      case wParam = HTCAPTION of
        TRUE: Showwindow(hwnd, SW_MINIMIZE);
      end;
  else
    if ((MsgTaskbarcreated <> 0) and (uMsg = MsgTaskbarcreated)) then
    begin
      DestroyTrayIcon;
      CreateTrayIcon(hwnd);
    end
    else
      Result := False;
  end;
end;

function InitPortArray(fname: string): PPortArray;
var
  f: Textfile;
  s: string;
  Port: Word;
  Code: Integer;
begin
  result := nil;
  AssignFile(f, fname);
{$I-}
  Reset(f);
{$I+}
  if IOResult = 0 then
  try
    GetMem(result, sizeof(result^));
    if Assigned(result) then
    begin
      ZeroMemory(result, sizeof(result^));
      while not EOF(f) do
      begin
        Readln(f, s);
        if s[1] = '#' then
          continue;
        val(copy(s, 1, 5), Port, Code);
        if Code = 0 then
          result^[Port] := copy(s, 7, length(s));
      end;
    end;
  finally
    CloseFile(f);
  end;
end;

begin
// Warning on Win9x
  if GetVersion and $80000000 <> 0 then
  begin
    MessageBox(0, 'Note: There are certain limitations on Windows 9x/Me which might' +
      ' cause the program to hang or not work at all! Be warned! You use it at your own risk.', 'Information', MB_ICONWARNING);
    numthreads := $F;
  end;
// Create critical section
  InitializeCriticalSection(cs);
// Init comctl library
  InitCommonControls;
  try
// Init WinSock (v 1.1)
    if WSAStartup(MAKEWORD(1, 1), wsa) = 0 then
    try
      icons[0] := LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
      icons[1] := LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON2));
// Init port list
      portdesc := InitPortArray(portfile);
// Start main window
      DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), 0, @dlgfunc, 0);
    finally
// Finit WinSock
      WSACleanup;
    end
    else
      MessageBox(0, 'Found no appropriate WinSock support. Version 1.1 needed.', nil, 0);
  finally
// Remove critical section
    DeleteCriticalSection(cs);
// Free the port list
    if Assigned(portdesc) then
      FreeMem(portdesc);
  end;
end.
搜索更多相关主题的帖子: ports 搜索 customize request include 
2012-04-14 22:35
快速回复:如何改成可以搜索多IP,例如扫描192.168.0.0到192.168.10.10之间的端口 ...
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.081044 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved