PInvoike InternetGetCookieEx2 returns Error 12006

57 Views Asked by At

I am trying to retrieve cookies using c#, but I get an error 12006 complaining about the URL. I use InternetGetCookieEx successfully to retrieve cookie data, but I need to retrieve the entire cookie for my current exercise

I'm not sure If I call it incorrectly, or my flags is not correct, but there are only 4 flags, and 2 of them is retrictive. My problem I believe is the pointer of the array of INTERNET_COOKIE2 that needs to returned. I am really stuck here and need some guidance. This is my code

  [DllImport("wininet.dll", SetLastError = true)]
  internal static extern bool InternetGetCookieEx2(
          string url,
          string cookieName,
          Int32 dwFlags,
          INTERNET_COOKIE[] cookie,
          ref Int32 cookieCount);

  [SecurityCritical]
  public static Cookie[] GetCookieInternal(Uri uri, bool throwIfNoCookie)
  {
     int cookieCount = 0;
     string url = UriToString(uri);
     int flag = 0;

     DemandWebPermission(uri);
     //There is no error here, but cookieCount comes back as 0. If I ommit this code and
     //pass in the array directly I get the 12006 error. url is valid. I've checked multiple times
     if (CookieHelper.InternetGetCookieEx2(url, null, flag, null, ref cookieCount))  
     {
        if (cookieCount > 0)
        {
           CookieHelper.INTERNET_COOKIE[] pchCookies = new CookieHelper.INTERNET_COOKIE[cookieCount];
           if (CookieHelper.InternetGetCookieEx2(url, null, flag, pchCookies, ref cookieCount))
           {
              var result = new Cookie[cookieCount];
              for (int i = 0; i < cookieCount; i++)
              {
                 result[i] = new Cookie
                 {
                    HttpOnly = (pchCookies[i].dwFlags & (int)CookieHelper.InternetFlags.INTERNET_COOKIE_HTTPONLY) > 0,
                    Domain = pchCookies[i].pwszDomain,
                    Expired = pchCookies[i].fExpiresSet,
                    Expires = pchCookies[i].fExpiresSet ? DateTime.FromFileTime(pchCookies[i].ftExpires.dwHighDateTime) : DateTime.MinValue,
                    Name = pchCookies[i].pwszName,
                    Path = pchCookies[i].pwszPath,
                    Secure = (pchCookies[i].dwFlags & (int)CookieHelper.InternetFlags.INTERNET_COOKIE_IS_SECURE) > 0,
                    Value = pchCookies[i].pwszValue
                 };
              }
              return result;
           }
        }
     }

     int lastErrorCode = Marshal.GetLastWin32Error();

     if (throwIfNoCookie || (lastErrorCode != (int)CookieHelper.ErrorFlags.ERROR_NO_MORE_ITEMS))
     {
        throw new Win32Exception(lastErrorCode);
     }

     return null;
  }

  private static void DemandWebPermission(Uri uri)
  {
     string uriString = UriToString(uri);

     if (uri.IsFile)
     {
        string localPath = uri.LocalPath;
        new FileIOPermission(FileIOPermissionAccess.Read, localPath).Demand();
     }
     else
     {
        new WebPermission(NetworkAccess.Connect, uriString).Demand();
     }
  }

  private static string UriToString(Uri uri)
  {
     if (uri == null)
     {
        throw new ArgumentNullException("uri");
     }

     UriComponents components = (uri.IsAbsoluteUri ? UriComponents.AbsoluteUri : UriComponents.SerializationInfoString);
     return new StringBuilder(uri.GetComponents(components, UriFormat.SafeUnescaped), 2083).ToString();
  }

UPDATE Working Code Here is my code for anyone in the future.

public class CookieHelper
{
  [DllImport("wininet.dll", CharSet = CharSet.Unicode)]
  internal static extern int InternetGetCookieEx2(
          string url,
          string cookieName,
          int dwFlags,
          [Out] out IntPtr cookie,
          [Out] out int cookieCount);


  [DllImport("wininet.dll")]
  internal static extern void InternetFreeCookies(
            IntPtr pCookies,
            int dwCookieCount);

  public enum InternetFlags
  {
     INTERNET_COOKIE_IS_SECURE = 32,
     INTERNET_COOKIE_HTTPONLY = 8192, //Requires IE 8 or higher   
     INTERNET_COOKIE_THIRD_PARTY = 131072,
     INTERNET_FLAG_RESTRICTED_ZONE = 16
  }

  public struct INTERNET_COOKIE
  {
     public IntPtr pwszName;
     public IntPtr pwszValue;
     public IntPtr pwszDomain;
     public IntPtr pwszPath;
     public int dwFlags;
     public System.Runtime.InteropServices.ComTypes.FILETIME ftExpires;
     public bool fExpiresSet;
  }
}

public class FullWebBrowserCookie
{
  /// <summary>
  /// Internal Get Cookie
  /// </summary>
  /// <param name="uri"></param>
  /// <param name="throwIfNoCookie"></param>
  /// <returns></returns>
  [SecurityCritical]
  public static Cookie[] GetCookieInternal(Uri uri, bool throwIfNoCookie)
  {
     string url = UriToString(uri);
     int flag = 0;  //Change to 4096 if you want to retrieve HTTP Only cookies

     DemandWebPermission(uri);
     var error = CookieHelper.InternetGetCookieEx2(url, null, flag, out var ptr, out var cookieCount);
     if (error != 0)
        throw new Win32Exception(error);
     if (ptr == IntPtr.Zero)
     {
        if (throwIfNoCookie)
           throw new InvalidOperationException("No cookie");

        return Array.Empty<Cookie>();
     }
     try
     {
        if (throwIfNoCookie && cookieCount == 0)
           throw new InvalidOperationException("No cookie");
        var result = new Cookie[cookieCount];
        var size = Marshal.SizeOf<CookieHelper.INTERNET_COOKIE>();

        for (int i = 0; i < cookieCount; i++)
        {
           var pchCookie = Marshal.PtrToStructure<CookieHelper.INTERNET_COOKIE>(ptr + i * size);
            result[i] = new Cookie
            {
               HttpOnly = (pchCookie.dwFlags & (int)CookieHelper.InternetFlags.INTERNET_COOKIE_HTTPONLY) > 0,
               Domain = Marshal.PtrToStringAuto(pchCookie.pwszDomain),
               Expires = pchCookie.fExpiresSet ? DateTime.FromFileTime((((long)pchCookie.ftExpires.dwHighDateTime) << 32) + pchCookie.ftExpires.dwLowDateTime) : DateTime.MinValue,
               Expired = pchCookie.fExpiresSet && DateTime.FromFileTime((((long)pchCookie.ftExpires.dwHighDateTime) << 32) + pchCookie.ftExpires.dwLowDateTime) < DateTime.Now,
               Name = Marshal.PtrToStringAuto(pchCookie.pwszName),
               Path = Marshal.PtrToStringAuto(pchCookie.pwszPath),
               Secure = (pchCookie.dwFlags & (int)CookieHelper.InternetFlags.INTERNET_COOKIE_IS_SECURE) > 0,
               Value = Marshal.PtrToStringAuto(pchCookie.pwszValue)
            };
        }
        return result;
     }
     finally
     {
        CookieHelper.InternetFreeCookies(ptr, cookieCount);
     }

  }

  private static void DemandWebPermission(Uri uri)
  {
     string uriString = UriToString(uri);

     if (uri.IsFile)
     {
        string localPath = uri.LocalPath;
        new FileIOPermission(FileIOPermissionAccess.Read, localPath).Demand();
     }
     else
     {
        new WebPermission(NetworkAccess.Connect, uriString).Demand();
     }
  }

  private static string UriToString(Uri uri)
  {
     if (uri == null)
     {
        throw new ArgumentNullException("uri");
     }

     UriComponents components = (uri.IsAbsoluteUri ? UriComponents.AbsoluteUri : UriComponents.SerializationInfoString);
     return new StringBuilder(uri.GetComponents(components, UriFormat.SafeUnescaped), 2083).ToString();
  }

}
1

There are 1 best solutions below

2
Charlieface On BEST ANSWER

You seems to have mixed up the docs for InternetGetCookieEx2 with the docs for InternetGetCookieEx.

You have a number of issues:

  • The return code is the error itself. InternetGetCookieEx2 does not set the last error code, it just returns it.
  • Need to specify CharSet.Unicode.
  • The pointer to the buffer is not one that you supply. It's a pointer to a pointer to the buffer, and you need to free that buffer. InternetGetCookieEx2 is not documented to return partial results, you will get everything in a single call.
  • Therefore you also cannot use automatic marshalling. You need to use PtrToStructure manually.
  • You also need to add Unicode to your struct, so that the strings are marshalled correctly.
[DllImport("wininet.dll", CharSet = CharSet.Unicode)]
internal static extern int InternetGetCookieEx2(
          string? url,
          string? cookieName,
          int dwFlags,
          [Out] out IntPtr cookie,
          [Out] out int cookieCount);

[DllImport("wininet.dll")]
internal static extern void InternetFreeCookies(
          IntPtr pCookies,
          int dwCookieCount);

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct INTERNET_COOKIE2
{
    public string pwszName;
    public string pwszValue;
    public string pwszDomain;
    public string pwszPath;
    public int dwFlags;
    public System.Runtime.InteropServices.ComTypes.FILETIME ftExpires;
    public bool fExpiresSet;
}
[SecurityCritical]
public static Cookie[] GetCookieInternal(Uri uri, bool throwIfNoCookie)
{
    string url = UriToString(uri);
    int flag = 0;

    DemandWebPermission(uri);
    var error = CookieHelper.InternetGetCookieEx2(url, null, flag, out var ptr, out var cookieCount);
    if (error != 0)
        throw new Win32Exception(error);
    if (ptr == IntPtr.Zero)
    {
        if (throwIfNoCookie)
            throw new InvalidOperationException("No cookie");

        return Array.Empty<Cookie>();
    }

    try
    {
        if (throwIfNoCookie && cookieCount == 0)
            throw new InvalidOperationException("No cookie");

        var result = new Cookie[cookieCount];
        var size = Marshal.SizeOf<CookieHelper.INTERNET_COOKIE>();
        for (int i = 0; i < cookieCount; i++)
        {
            var pchCookie = Marshal.PtrToStructure<CookieHelper.INTERNET_COOKIE>(ptr + i * size)
            result[i] = new Cookie
            {
                HttpOnly = (pchCookie.dwFlags & (int)CookieHelper.InternetFlags.INTERNET_COOKIE_HTTPONLY) > 0,
                Domain = pchCookie.pwszDomain,
                Expired = pchCookie.fExpiresSet,
                Expires = pchCookie.fExpiresSet ? DateTime.FromFileTime(pchCookie.ftExpires.dwHighDateTime) : DateTime.MinValue,
                Name = pchCookie.pwszName,
                Path = pchCookie.pwszPath,
                Secure = (pchCookie.dwFlags & (int)CookieHelper.InternetFlags.INTERNET_COOKIE_IS_SECURE) > 0,
                Value = pchCookie.pwszValue
            };
        }
        return result;
    }
    finally
    {
        InternetFreeCookies(ptr, cookieCount);        
    }
}