HTTP post cannot reach host in very specific situations - vb.net

My company posts invoices to both Ariba and Oracle Supplier Networks via HTTP POST. We've had an issue where we haven't been able to determine whether it's settings or code.
Below is the code:
Public Function SendMime(pFiles As List(Of String)) As String
'just use the text send option in HTTP connection
If pFiles.Count = 1 And pFiles.Item(0).EndsWith(".xml") Then
Return Send(My.Computer.FileSystem.ReadAllText(pFiles(0)))
End If
Dim Request As Net.HttpWebRequest
Request = DirectCast(Net.WebRequest.Create(GetFirstURL()), Net.HttpWebRequest)
Request.ContentType = "multipart/related;boundary=" & OuterBoundary & ";type=""text/xml"";start=""<" & _CID & ".1>"""
Request.Method = "Post"
Request.KeepAlive = True
WriteRequest(Request, pFiles)
Dim Response As Net.WebResponse
Try
Response = Request.GetResponse()
Dim ResponseStream As Stream = Response.GetResponseStream()
Dim ResponseReader As New StreamReader(ResponseStream)
Return ResponseReader.ReadToEnd()
Catch ex As System.Exception
Return "error: " & ex.Message
End Try
End Function
Private Sub WriteRequest(pRequest As Net.WebRequest, pFiles As List(Of String))
Dim PartCount As Integer = 1
_RequestStream = pRequest.GetRequestStream()
_tempStream = New FileStream("C:\lastMIMEsent.txt", FileMode.Create)
For Each File As String In pFiles
WriteBoundary(OuterBoundary)
If File.ToLower.EndsWith(".xml") Then
GetXMLPart(File, PartCount)
ElseIf File.ToLower.EndsWith(".pdf") Then
GetPDFPart(File, PartCount)
End If
PartCount += 1
Next
WriteTrailer(OuterBoundary)
_RequestStream.Close()
_tempStream.Flush()
_tempStream.Close()
End Sub
The send function is similar to the SendMime function, but with no attachments (cxml only). This code is shared between a Windows Service and a basic GUI test program.
edit: The error I get is: "Unable to connect to host."
Where things get weird:
On the server running the service: Oracle works, Ariba does not, when the service calls the SendMime function.
On the server running the service: Both work, when the GUI utility calls the SendMime function.
On my work station: both work, regardless of whether it is the service or GUI calling the SendMime function
My first thought was Proxy, but both machines are behind the same proxy.
My second thought was Firewall issues, but we disabled the firewall with no success.
We can connect to the address via web browser as well.
We tried with different Active Directory accounts, with no change as well.
Both the service and the GUI utility are running .Net 4.5 (needed for some asynchronous parts of the service). They were not at one point, but upgrading the Utility made no difference.
The only difference i can find, is that my machine runs Windows 7 and the server runs Windows Server R2, but I can find no documentation stating that there should be a difference.
I've been trying to figure this out for a couple weeks, experimenting with different adjustments, and at this point, both I and the server team are out of ideas. Any advice, whether its red flags in my code or just known windows server issues, would be greatly appreciated.

Related

Using Self Code Signing Certificates for Publish purpose

I searched many times in this topic but with no luck, I have an application on WPF that use Click Once to published and there fore I can't use Admin Privilege, and I need to implement some actions in the installation Process like restart SQL server for example and for that I need Admin Privilege and since i start publishing using click once I had to remove Admin Privilege now, i tried restart my application to force Admin rights using the following code but didn't work
Public Function IsRunAsAdmin() As Boolean
Try
Dim id As WindowsIdentity = WindowsIdentity.GetCurrent()
Dim principal As WindowsPrincipal = New WindowsPrincipal(id)
Return principal.IsInRole(WindowsBuiltInRole.Administrator)
Catch __unusedException1__ As Exception
Return False
End Try
End Function
Public Sub AdminRelauncher()
If Not IsRunAsAdmin() Then
Dim proc As ProcessStartInfo = New ProcessStartInfo()
Dim procExecuting As New Process
proc.UseShellExecute = True
proc.WorkingDirectory = Environment.CurrentDirectory
proc.FileName = Assembly.GetEntryAssembly().CodeBase
proc.Verb = "runas"
Try
procExecuting = Process.Start(proc)
Application.ExitThread()
Application.Exit()
Catch ex As Exception
Console.WriteLine("This program must be run as an administrator! " & vbLf & vbLf & ex.ToString())
End Try
End If
End Sub
I still get the Error "Can not open MSSQL$****** on computer" after i searched of course i found the cause is admin rights needed.
So now i'm working on Code Signing Certificates as some one advise me that this will work for me.
but i'm an individual and my application price is not high enough to buy a paid certificate so i was wondering if i can make a self Code Signing Certificate and use it with click once.
Thank you.
I finally manged to figure out the problem my self, it was the admin rights.
The problem was that the code simply need like 2-3 mile seconds to execute so when i tracked the code i found that my code manged to stop the service indeed but wasn't able to start it again as it didn't take the time for the service to fully stop.
The solution was simple as it just needed couple of seconds to after it execute the code to stop the service.
So simply need to add a code in between.
First
Imports System.Threading
Then use the code as following
Dim service As ServiceController = New ServiceController("SQL Server (SQLEXPRESS)")
service.Stop()
Thread.Sleep(10000)
service.Start()
You see the code where Thread.sleep is make the application wait 10 seconds before it complete to execute the code.
This time is depends on the machine it self, 2-3 sec was enough but some other machine needed more time so to be in the safe side just add more seconds as I did.
Thank you, and "Stay Safe".

MS-Access in Sharepoint - what data available?

For better or worse we are launching an Access db from Sharepoint. Note that this db is not PUBLISHED to SP, people just double-click the link and open the db on their desktops.
So now we need to begin imposing the equivalent of some roles-based edit restrictions. I know there is a VBA CurrentWebUser function and a CurrentWebUserGroups which provides some basic data about who's accessing an Office file from Sharepoint. However my reading and limited experimenting with this stuff leads me to suspect that, for Access at least, these will only work with published dbs, and not ones that are just being launched and run locally, like we're doing.
Is there anything I can get from SP in a case like this? Web user and user group would be useful, so would whichever site/page the link is being clicked on. Is any of this available?
Thanks.
rabbit
Well, not in any simple way.
As you've already determined, Application.CurrentWebUser just returns Null.
However, there are several ways to query the user information from SharePoint.
The recommended way (also by me) if you're going to work with SharePoint extensively, is to use the CSOM api, which requires a .Net language, so you'll have to create a COM module, authenticate it separately, and that's all a lot of work.
However, if you're only using simple GET requests, you can also use the REST API and re-use the authentication MS Access uses itself (since MS Access uses MSXML2 to submit web requests to SharePoint, we can create our own MSXML2.XMLHTTP object and it will re-use the cookies Access uses).
The following code uses the JSONInterpreter object I've shared here on GitHub. You could convert it to use XML and MSXML if you don't want that dependency, though.
To execute a request, I use the following code, that assumes the Access application is authenticated, but if it isn't, it connects to the SharePoint site using ADO.
(For this code, MySiteName is a global variable containing the URL of your SharePoint site, without a trailing slash)
Public Function SPRestGetJSON(Site As String, Request As String) As String
Dim tries As Long
Dim Success As Boolean
Do
'Try to execute request
tries = tries + 1
Dim xmlHttpReq As Object 'MSXML2.XMLHTTP60
Set xmlHttpReq = CreateObject("Msxml2.XMLHTTP.6.0") 'New MSXML2.XMLHTTP60
xmlHttpReq.Open "GET", Site & Request, False
xmlHttpReq.setRequestHeader "Content-Type", "application/json"
xmlHttpReq.setRequestHeader "Accept", "application/json;odata=nometadata"
xmlHttpReq.send
Dim root As JSONInterpreter
Set root = New JSONInterpreter
root.JSON = xmlHttpReq.responseText
If Not root.Exists("odata.error") Then
Success = True
End If
If Not Success And tries = 1 Then
'Connect to SharePoint using WSS + ADO to create auth cookies inside MSXML
Dim conn As Object 'ADODB.Connection
Set conn = CreateObject("ADODB.Connection") 'New ADODB.Connection
conn.Open "Provider=Microsoft.ACE.OLEDB.12.0;WSS;DATABASE=" & Site
On Error Resume Next
conn.Execute "SELECT 1 From SomeTable" 'Execute to non-existent table but connect to sharepoint
On Error GoTo 0
conn.Close
End If
Loop While tries < 2 And Success = False
SPRestGetJSON = xmlHttpReq.responseText
End Function
Then, we can use that in a simple function:
Public Function GetSPUsername() As String
Dim jsi As New JSONInterpreter
jsi.JSON = SPRestGetJSON(MySiteName, "/_api/Web/CurrentUser")
GetSPUsername = jsi.item("LoginName").VBAVariant
End Function
Getting groups is also available. This code returns an array of dictionary objects, you can view the available keys in the locals window:
Public Function GetSPGroups() As Variant 'Array of dictionaries
Dim jsi As New JSONInterpreter
jsi.JSON = SPRestGetJSON(SiteName, "/_api/Web/CurrentUser/Groups")
GetSPGroups = jsi.item("value").VBAVariant
End Function
Then, to get the title of the first group the current user is a member of in the immediate window, we can use:
?GetSPGroups(0)!Title

Is there a direct way to copy files from ServerA to ServerB when only one of them requires impersonation?

I am trying to copy files from Server A to Server B.
So basically, it's fast and easy, File.Copy.
File.Copy("\\ServerA\c$\CopyImageTest\Server61.txt", "\\ServerB\c$\CopyImageTest\Server61.txt")
File.Copy("\\ServerB\c$\CopyImageTest\Server182.txt", "\\ServerA\c$\CopyImageTest\Server182.txt")
Since Server A requires login to use, it's obvious that I got this error:
System.IO.IOException: Logon failure: unknown user name or bad password.
I've looked around and found that I should use Impersonation in order to access that server and copy the files. I found this answer which is in C# and converted it to VB.NET.
Here's the code that I am using:
Module Module1
<PermissionSetAttribute(SecurityAction.Demand, Name:="FullTrust")> _
Sub Main()
Try
'Some declarations omitted for clearance.
Const LOGON32_PROVIDER_DEFAULT As Integer = 0
Const LOGON32_LOGON_INTERACTIVE As Integer = 2
Const LOGON32_LOGON_NEW_CREDENTIALS As Integer = 9
Try
Dim bImpersonated As Boolean = LogonUser(sUsername, sDomain, sPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, pExistingTokenHandle)
If (Not bImpersonated) Then
Dim nErrorCode As Integer = Marshal.GetLastWin32Error()
Return
End If
Dim bRetVal As Boolean = DuplicateToken(pExistingTokenHandle, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, pDuplicateTokenHandle)
If (Not bRetVal) Then
Dim nErrorCode As Integer = Marshal.GetLastWin32Error()
CloseHandle(pExistingTokenHandle)
Return
Else
Dim newId As WindowsIdentity = New WindowsIdentity(pDuplicateTokenHandle)
Dim impersonateduser As WindowsImpersonationContext = newId.Impersonate()
'Shouldn't I be copying now?
File.Copy("\\ServerA\c$\CopyImageTest\Server61.txt", "\\ServerB\c$\CopyImageTest\Server61.txt")
File.Copy("\\ServerB\c$\CopyImageTest\Server182.txt", "\\ServerA\c$\CopyImageTest\Server182.txt")
sResult = sResult & "After impersonation: " & WindowsIdentity.GetCurrent.Name & Environment.NewLine
impersonateduser.Undo()
End If
Catch ex As Exception
'Omitted
Finally
If (Not pExistingTokenHandle = IntPtr.Zero) Then
CloseHandle(pExistingTokenHandle)
End If
If (Not pDuplicateTokenHandle = IntPtr.Zero) Then
CloseHandle(pDuplicateTokenHandle)
End If
End Try
Catch ex As Exception
'Omitted
End Try
End Sub
' DLL Imports and Enum Omitted.
End Module
Some code to output the results has been omitted, but here's the results from the console:
User was able to login. (This is used after LogonUser()) (Previously it was throwing an error (1326) Logon failure: unknown user name or bad password)
Before impersonation: User-PC\User (Result of WindowsIdentity.GetCurrent().Name; Before Impersonate())
After impersonation: User-PC\User (Result of WindowsIdentity.GetCurrent().Name; After Impersonate())
System.IO.IOException: Logon failure: unknown user name or bad password.
What's confusing is that the name before and after impersonation is the same as my local PC. However the login is working. (I am positive about that because, as stated, it was failing previously.)
Secondly, the file still doesn't get copied with same error as before:
Logon failure: unknown user name or bad password.
Is this how actually Impersonation should be working, or am I missing something obvious?
After digging a bit more, I also found this answer and I used the same exact code from the answer, but with no luck at all.
Some information about the Server A that I am trying to access and copy the files from:
I can connect through remote desktop connecting using the same username/password.
I can open the file explorer from my own PC using the server IP address as shown in the code: \\ServerA\c$\CopyImageTest.
Both my PC and the server are on the same network, but different domains.
Update:
#Esko's comment guided me to make two or three tests.
Here's the scenario:
With Impersonation I tried this code:
File.Copy("\\ServerA\c$\CopyImageTest\Server61.txt", "C:\CopyImageTest\Server61.txt")
File.Copy("C:\CopyImageTest\Server182.txt", "\\ServerA\c$\CopyImageTest\Server182.txt")
Which means that I only copied the files from ServerA to local and vice versa.
Result: Passed.
With Impersonation
File.Copy("C:\CopyImageTest\Server61.txt", "\\ServerB\c$\CopyImageTest\Server61.txt")
File.Copy("\\ServerB\c$\CopyImageTest\Server182.txt", "C:\CopyImageTest\Server182.txt")
Which means I tried to copy from ServerB to local and vice verca
Result: Failed
Without impersonation
File.Copy("C:\CopyImageTest\Server61.txt", "\\ServerB\c$\CopyImageTest\Server61.txt")
File.Copy("\\ServerB\c$\CopyImageTest\Server182.txt", "C:\CopyImageTest\Server182.txt")
Copying from and to ServerB
Result: Passed
So at the end, I think the problem is that when Impersonating the user to access ServerA it fails to access ServerB, knowing that ServerB doesn't require username password when trying to access it from my PC. Maybe because my PC and ServerB uses same domain on same network but ServerA uses another domain on same network?
Is there any workaround to access both servers with a single impersonation?
Note: I am aware of being able to copy files from ServerA to local then undo impersonation and copy to ServerB, I am just looking for a more straight way.

TCP/IP Messaging Application VB.net - IP Address / Port-Forwarding Questions

I am developing a piece of scientific software in VB.net that I intend to distribute. Part of the application will involve communication with a server (and other clients). For all intents and purposes the communication will be like a chat application.
I have developed the software using sockets, and it works fine when testing at home on my local network. Below is basically how I send messages:
Dim serverIPString as String = "192.168.1.5"
Dim serverPort as long = 65500
dim messageString as String = "Hello"
Dim Client As New System.Net.Sockets.TcpClient(serverIPString, serverPort)
Dim Writer As System.IO.StreamWriter(Client.GetStream())
Writer.Write(messageString)
Writer.Flush()
Writer.Close()
And this is how I listen:
Public Class TCPIPListener
Dim initalised As Boolean = False
Dim port As Long
Dim IP As System.Net.IPAddress
Dim ourListener As TcpListener
Dim ourClient As TcpClient
Dim ourMessage As String
'''''''''''
'' ctors ''
'''''''''''
Public Sub New()
initalised = False
End Sub
'Takes IP and port and starts listeing
Public Sub New(inIP As String, inPort As Long, inExchange As messageExchange)
'Try to set the IP
Try
IP = System.Net.IPAddress.Parse(inIP)
Catch ex As Exception
Exit Sub
End Try
'Set the port
port = inPort
initalised = startListener()
End Sub
'''''''''''''''
'' Listening ''
'''''''''''''''
Private Sub Listening()
ourListener.Start()
End Sub
'starts listener
Public Function startListener() As Boolean
ourListener = New TcpListener(IP, port)
ourClient = New TcpClient()
Dim ListenerThread As New Thread(New ThreadStart(AddressOf Listening))
ListenerThread.Start()
initalised = True
Return True
End Function
'Called from a timer on the form
Public Function tick() As Boolean
If Not initalised Then
Return False
End If
If ourListener.Pending = True Then
ourMessage = ""
ourClient = ourListener.AcceptTcpClient()
Dim Reader As New System.IO.StreamReader(ourClient.GetStream())
While Reader.Peek > -1
ourMessage = ourMessage + Convert.ToChar(Reader.Read()).ToString
End While
messagebox(ourMessage)
End If
Return True
End Function
End Class
Using this approach, every client will be listening for messages sent from the server, and any messages sent will go to the server and be directed to the relevant client - it's a little inefficient but necessary for the way my software works.
The problem is that I am having real trouble getting this to work across the internet. If I send a message to a local IP address it works fine, even from the debugger. If I use a external IP address I get the following:
No connection could be made because the target machine actively refused it XXX.XXX.XXX.XXX:65500"
I have turned on port forwarding with my router and set up the correct port (65500 here) to forward to my PC running the lister (192.168.1.5) for both TCP and UDP. This website it tells me this port is open, and those either side are not.
If I right click on the exe and run as administrator, my antivirus pops up and says it is doing something suspicious when I start the listener (which I guess if anything is encouraging). If I then add my application to the list of exceptions and run it again, then check to see if the port is listening (using this) I find my application listening on the correct port (if I stop the application it is gone). However the client application still gives the same error above. If I use a random IP address rather than the one of the listener I get a different error:
{"A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond XXX.XXX.XXX.XXX:65500"}
I suspect I am not taking the correct approach to this. I am fine with most aspects of VB, but have not really tackled this sort of problem before. When it comes to distributing software I really don't want my customers to have to mess around with deep settings to get this application to work. Any help will be immensely useful to me!
Another problem I guess I will face in the future is if multiple clients have the same external IP but different internal IPs (I intend to distribute to universities). Under these circumstances I guess port forwarding will not work.
Thanks very much in advance (and apologies for the long post)!
If you set your server to listen for connections on 192.168.1.5 then it will only allow connections from that specific IP address. The address you give a TcpListener is the IP address which you will accept connections from.
Since the 192.168.1.5 address is internal no one outside your network will be able to connect to your server. Make your server listen to 0.0.0.0 (or the equivalent IPAddress.Any) instead to make it allow connections from anyone.

Unable to execute SQL command in windows service

I'm working on creating a windows service that will send emails to a customer when they are within a month of having their submission expire. I'm using vb.net in Visual Studios 2008
Because it's a windows service it's very difficult to debug. Trying to narrow down my error I created a "sendDebugEmail" method that sends me an email if it gets to a certain line. The emails never make it past "dr = cmd.ExecuteReader()"
I'm wondering what I am doing wrong. My SQL statement should work fine. I've tested it in my SQL server database.
I've created a dummy_database that I just made in sql server as well. I added an INSERT sql statement for the dummy table i have in there just to see if i could actually access a database. All the table takes in is the line number and time it was sent. When I run my windows service that database updates just fine.
Any help would be appreciated. Thanks
Dim conn As New SqlConnection(connString2)
sendDebugEmail("134")
SQL = "Select email FROM _Customer WHERE custID in (SELECT custID FROM _OnlineCustomer WHERE ExpirationDate <= '6-20-12' AND ExpirationDate >= '6-10-12')"
Dim cmd As New SqlCommand(SQL, conn)
sSubject = "hello"
sBody = "This is test data"
Dim dr As SqlDataReader
sendDebugEmail("143")
Try
dr = cmd.ExecuteReader() // This is were it stops
sendDebugEmail("147")
While dr.Read
sendDebugEmail("152")
Try
LogInfo("Service woke up")
Dim i As Integer = 0
' Prepare e-mail fields
sFrom = "test#gmail.com"
sTo = "test1#gmail.com"
sCc = "test2#gmail.com"
Dim oMailMsg As MailMessage = New MailMessage
oMailMsg.From = sFrom
oMailMsg.To = sTo
oMailMsg.Cc = sCc
' Call a stored procedure to process the current item
' The success message
oMailMsg.Subject = sSubject + "(Success)"
oMailMsg.Body = sBody + "Email has been sent successfully."
' Send the message
If Not (oMailMsg.To = String.Empty) Then
SmtpMail.Send(oMailMsg)
End If
Catch obug As Exception
LogEvent(obug.Message)
Finally
End Try
End While
Catch ex As Exception
Finally
dr.Close()
cmd.Dispose()
conn.Close()
conn.Dispose()
End Try
End Sub
/////////////////////////////////////////////////////////////////////////////////
Problem Solved: I set up my connection but I never opened it.
I needed conn.open()
The thing that helped me most was adding this code into my last catch statement:
sendDebugEmail(ex.Message & vbcrlf & ex.stackTrace)
It send me an email of the stackTrace and made it very easy to debug
Are you trapping and swallowing exceptions? If you are, stop. Let exceptions service crash the service: the exception will be logged in the Event log. The only exceptions you should trap are those you can actually recover from (though its valid to catch the exception, log it and rethrow it via throw;).
Have you instrumented your code with log4net (http://logging.apache.org/log4net/), or something similar? You should be, especially for a daemon like a Windows service — how else are you (or operations) going to diagnose problems with the service when the occur (as they will).
Edited to note:
You should be using using statements: all those ADO.Net objects are IDisposable. It makes for cleaner code.
Consider using a SqlDataAdapter to fill a DataTable or DataSet with your results set. The pattern you're using:
read a row from SQL
while read was successful
send an email
read a row from SQL
will ultimately lead to blocking in your database. Talking to a mail server has the potential for a high latency. If the mail server doesn't answer, or you have network congestion, or any of a number of other reasons, you're going to be left hanging until the mail is sent or an exception is thrown due to timeout. And your SQL Query is going to be sitting there with read locks on the table and indices from which you're reading data, blocking people attempting to do updates, inserts or deletes. Your production DBAs will be...vexed. You want to keep your locks moving and get the locks released as quick as you can.
If you are sure about your code (with no exceptions) i think you should check the authentication you are using to connect the SQL server(inside the connection string within the app.config file/inline code of the windows service).
If you are using SQL authentication for this (check the connection string for user name sa and its password) setting the account type of the windows service to LocalService will help you.
If the SQL connection is using windows authentication then setting the account type of the windows service to LocalSystem will help you.
The Account type modification can be done after installation also. For this go to Control panel->Administrative tools->Services->YourService right click and select Propertes->Logon and perform it there. If you are selecting the LocalSystem (windows authentication) you will be asked to enter the login credentials of the account in which the service is running.
In the case of windows authentication in SQL connection the credentials of the account in which the service is running will be taken for SQL connectivity also.
Hope this helps ...
One more suggestion put a sleep statement on your process when it starts so oyu have time to attach to it
Problem Solved: I set up my connection but I never opened it.
I needed conn.open()
The thing that helped me most was adding this code into my last catch statement:
sendDebugEmail(ex.Message & vbcrlf & ex.stackTrace)
It send me an email of the stackTrace and made it very easy to debug