Unsupported .Net SDK version error in Visual Studio Code extension

During the last couple of days, my VSCode installation greeted me with a strange error message on startup:

Installed .NET SDK version 6.0.100-preview.7.21379.14 is newer than the currently supported versions. Project build will not work. Please install .NET Core SDK version 3.1 and include a global.json in the project folder specifying the SDK version to use. More Information

Recently installed .Net SDK 6

VSCode is currently version 1.62.2 (it auto-updated today), and the extension version data is:
– Released on: 28.10.2021, 00:30:16
– Last updated: 28.10.2021, 00:32:34
– Identifier: ms-mssql.sql-database-projects-vscode

The More Information link points to the page Select the .NET version to use and does not seem to apply to installed VSCode extensions.

After a bit of digging, I found a couple of GitHub issues in the azuredatastudio project, which the SQL Database Projects extension is part of.

Issue 16766 deals with the behavior I noticed: SQL projects don’t build when active dotnet version is 6.0 preview

and confirmed by one comment:

Unfortunately, this affects Visual Studio Code as well. Annoyingly, the warning pops up even if no database project is loaded.

I tried changing the extension settings “Sql Database Projects: Net Core SDKLocation” (Full path to .NET Core SDK on the machine) but this did not get rid of the error message.

So I navigated to the install location of the VSCode extension, which can be found under
%userprofile%\.vscode\extensions\ms-mssql.sql-database-projects-vscode-0.13.0
(adjust version number) and tried to figure out how the error message is raised.

It turns out that the extension does not specifically query for the required dotnet version, but rather issues a “dotnet –version”, which returns the highest installed version number:

> dotnet --version
6.0.100-preview.7.21379.14

This version number is checked against the hard-coded upper limit of

t.maxSupportedNetCoreVersionCutoff = "6.0.0";

So I opened the file extension.js and tried to change this value to a SemVer higher than “6.0.100”, such as “7.0.0”, and restarted VSCode. And … the error message was gone!

Ok, so the extension was enabled, and VSCode does not throw an error when it did before, but would the extension still actually work?

So I created a new SQL Database project by Ctrl+Shift+P and selecting “Database Projects: Create Project From Database”, subsequently entering all the connection string data to connect to a database, which resulted in a .sqlproj file and generated CREATE TABLE statements in one .sql file per table.

SMOscript 0.50

I added a couple of new options to my scripting tool SMOscript:

  • The new -td command-line switch generates two files for tables: a [table].create.sql file containing the CREATE TABLE statement, and a [table].details.sql file containing the CREATE statements all table details (foreign keys, indexes, triggers), thus allowing recreating of a database schema without referential integrity problems
  • The -o (object name) switch semantics has been extended to other commands than script
  • The new -ol command-line switch implements a filter on object names similar to T-SQL LIKE. The filter is applied on the object name, and if not matching, on the combination “schema.objectname”.

The new command dd operates on data diagrams (see my previous post on SQL Server Data Diagrams):

  • Using dd without parameters will list all data diagrams stored in the database.
  • Using dd -o [diagram name] will list all tables contained in the data diagram, even tables that have been DROPped.
  • Using dd -o [diagram name] -dt lists the DROPped tables only.

The latest version of SMOscript is available for download on my download website.

Debugging Ajax’ed JavaScript and jQuery val() calls

I develop a web application which displays data in a read-only form, and loads the edit form upon pressing a button

$(function () {
  $(".btnEdit").click(function () {
    $.ajax({
      url: '@Url.Action("Form", new { id = Model.ID })',
      type: 'post'
    });
  });
});

Now I needed to debug the JavaScript code loaded by the call to $.ajax(), but Chrome does not seem to display the loaded response in its tree of Sources.

An answer on SO provided me with the solution: Simply add the line

//# sourceURL=@Url.Action("Form", new { id = Model.ID })

inside a <script> block in the AJAX-loaded HTML. This will add the requested URL under the (no domain) node

chrome ajax js

Now that I have access to the source file, I needed to find all invocations of the jQuery val() function, since I was tracking down a wrong value in an <input>.

Again SO provided a solution, which I added to my code

(function($){
  var originalVal = $.fn.val;
  $.fn.val = function(){
    var selectorPath = $(this).parents().map(function () {return this.tagName;}).get().reverse().join("->");
    console.log("val( #" + $(this).attr("id") + " " + selectorPath + " , " + JSON.stringify(arguments) + ")");
    var result =originalVal.apply(this,arguments);
    return result;
  };
})(jQuery);

Now that the calls to val() were logged to the Console, it was easy to find where the wrong value was set.

Adding SSL Wildcard Certificates to IIS Webs

As web browsers start to issue warnings on plain http websites if you are asked to input username/password, it’s time to add SSL certificates even on dev/test servers. We can expect more aggressive warnings in the future 😉

Apparently there is a way to create a self-signed certificate built into IIS (screenshot from Windows Server 2008)

iis create certificate

but this seems to create cerficates only for the host name, not for any domain hosted on the machine.

Back to square one, start up a current Linux machine, and make sure your openssl is newer than version 1.0.1f. (Remember Heartbeed?).

The instructions I found to create self-signed certificates are nearly identical (source, source, source)

openssl genrsa 2048 > my-host.key
openssl req -new -x509 -nodes -sha1 -days 3650 -key my-host.key > my-host.cert
# make sure Common Name starts with "*.", e.g. *.my-host.com
openssl x509 -noout -fingerprint -text < my-host.cert > my-host.info
cat my-host.cert my-host.key > my-host.pem

For use in IIS, you need to create a .pfx from these certificate files:

openssl pkcs12 -inkey my-host.pem -in my-host.cert -export -out my-host.pfx

Copy the .pfx to your IIS machine.

In IIS Manager, select “Server Certificates” on the server node, click “Import…” to import the .pfx certificate.

Start up mmc, “File”, “Add/Remove Snap-in”, select “Certificates”, “Add”, “Computer account”, “Finish”, “OK”, (this click orgy shows you how important certificates were in 2008, as compared to Start/Administrative Tools/Data Sources (ODBC) 😉 ) and find the imported certificate(s) under

Console Root\Certificates\Personal\Certificates

Right-click each of them, select Properties, and make sure that the Friendly Name starts with “*.” for wild-card certificates. Otherwise, you cannot assign a host name for https web sites.

Back in IIS Manager, select each site you want to add https support, click Bindings, Add, select Type: https and select the wild-card SSL certificate. Only if the friendly name starts with *, you can/must set the site’s Host name. Click OK and you are done.

If you want your sites to redirect http to https automatically, make sure the Require SSL box is not checked in the site’s SSL Settings.

The minimal web.config to perform these redirects looks like this (source, source)

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Redirect-HTTP-HTTPS-IIS">
          <match url="(.*)" />
          <conditions>
            <add input="{HTTPS}" pattern="^OFF$" ignoreCase="true" />
          </conditions>
          <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" 
            redirectType="Permanent" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

Be aware that while these steps enable https for your IIS sites, self-signed certificates still require the users to explicitly accept the certificates in their browsers, which will raise an “Unknown issuer” warning at their first visit.

Update: There also seems to be a Powershell way to do it 😉

Installing AWStats on Windows Server 2012

To install AWStats on Windows, first download the current version from awstats.org. If you don’t have Perl on your machine, get Strawberry Perl for Windows, as ActivePerl requires an annual Business License for production use.

On the server, create a web directory and a data directory for awstats. Follow the steps in the AWStats Setup Guide.

To access the log files of a remote IIS, I created a read-only share on c:\inetpub\LogFiles, and had to run

icacls c:\inetpub\LogFiles /reset /t

to allow non-admin access to the IIS log files.

To get Strawberry Perl to run on IIS, follow this Installation Guide:

  • In the Web Server role, you need to have the CGI feature installed.
  • In IIS Administrator, create a web site or application hosting AWStats. In the site or application, you need to add a Script Map for *.pl executing
C:\path\to\perl.exe "%s" %s

Things should be running by now if you browse to

http://myHost/awstats/cgi-bin/awstats.pl?config=mySite

I noticed that the stats only included data from the installation date (IIS logs are configured to daily log files).

Answers on the internetz suggest to merge old log files using logresolvemerg.pl, a script that ships with awstats.

C:\awstats\tools>perl logresolvemerge.pl [all my log files] > merged.log

Replace the LogFile entry in your config file(s) to point to the merged log file

LogFile="C:\awstats-data\merged.log"
#LogFile="\\path\to\LogFiles\W3SVC1\u_ex%YY-1%MM-1%DD-1.log"

and run

perl awstats.pl -config=mySite

again after deleting the previously generated data files.

Unfortunately, the merged log only resulted in “dropped” and “corrupted” records:

Phase 1 : First bypass old records, searching new record...
Searching new records from beginning of log file...
Jumped lines in file: 0
Parsed lines in file: 30376
Found 16100 dropped records,
Found 0 comments,
Found 0 blank records,
Found 14276 corrupted records,
Found 0 old records,
Found 0 new qualified records.

This may be caused by a number of reasons, but it turned out that the merged log requires a specific LogFormat:

LogFormat="%time2 %other %method %url %query %other %logname %host %ua %code %other %other %other"

Finally, I created a batch file awstats.cmd to update all my statistics

net use z: \\host\LogFiles awstats /user:awstats
d:
cd D:\wwwroot\awstats\wwwroot\cgi-bin
perl awstats.pl -update -config=mySite1
perl awstats.pl -update -config=mySite2
...
net use z: /delete

and created a scheduled task to automatically execute the script every day.

Finding Spammers in hMailServer Log Files

hMailServer has a couple of spam protection measures built in, such as DNS blacklists and SURBL support. Among other features, you can also ban single IP adresses or IP ranges from connecting to your mail server.

While recently browsing through the log files, I noticed a couple of IP addresses which repeatedly connected to the mail server to log in, but kept their rate over the default 30 minutes auto-ban timer.

Interestingly those addresses chose to authenticate via AUTH LOGIN, but failed every time to provide a valid password. This results in a

535 Authentication failed

answer by the server, thus closing the conversation.

In the log file, the status code 535 looks like this

"SMTPD" 3228 21101 "2016-02-03 00:05:19.743" "xxx.xx.xx.xxx" 
  "SENT: 535 Authentication failed. Too many invalid logon attempts."

To find the conversations ending in status code 535, we can simply grep or findstr the relevant log files

grep "SENT: 535" *.log

In the log files, IP address is logged in the sixth column, so we can iterate over the resulting lines with the shell’s for command with option /f “tokens=6”.

Then we sort and count

(for /f "tokens=6" %i in ('grep "SENT: 535" *.log') do @echo %i) 
  | sort | uniq -c

To count the resulting IP addresses, I use my tool uniq, implemented after the Unix command uniq.

Similarly, one could also search for “550 Unknown user”.